+++ title = "Hello World!" summary = "Building your first blog using Hugo and Cloudflare Pages" date = "2024-04-22" tags = ["Hugo", "Cloudflare", "Pages", "Wrangler"] categories = ["Tutorial"] +++ *Hello World* is the first program a developer writes to check if their setup is working properly. As a kind of "Hello World" for my blog, I wanted to show how I created this site, starting from the Static Site Generator I chose - [Hugo](https://gohugo.io/) - and ending with showing how I hosted everything on Cloudflare using [Pages](https://pages.cloudflare.com/). ### Disclaimer {{< alert icon="triangle-exclamation" cardColor="#e6d237" iconColor="#1d3557" textColor="#1d3557" >}} Cloudflare? [**Absolutely proprietary**](/img/absolutely_proprietary.jpg)! {{< /alert >}} It might seem quite paradoxical that an open-source enthusiast would host their site on Cloudflare[^1] and even make a tutorial about it. [^1]: [old.reddit.com - ELI5 why CloudFlare is depicted as evil, and what's wrong with using their DNS (1.1.1.1)](https://old.reddit.com/r/privacy/comments/d52kop/) I just want to make clear that this is a temporary setup, suitable even for those who can't self-host a website, for security reasons or the CGNAT[^2] (*damned IPv4*). [^2]: [en.wikipedia.org - Carrier-grade NAT](https://en.wikipedia.org/wiki/Carrier-grade_NAT) For those interested, I'm making an article about how to forward the port through CGNAT, but for now let's continue using the damned Cloudflare. ## Creating a site with Hugo To manage all the content on this site, I use [Hugo](https://gohugo.io/), a static site generator that allows me to write pages and articles in a simple markup language, like Markdown. Hugo will then take the content and a theme and combine them, producing static HTML, CSS and JavaScript. To get started, you need to [install Hugo](https://github.com/gohugoio/hugo/releases/latest) and create a new project: ```shell hugo new site cd git init ``` If you want to view your site on a development server: ```shell hugo server ``` Now at the URL , you should see a *404: Page Not Found*. To fix this problem, we need to install a theme. ### Choosing a theme To get an idea of which themes we can install, we can go to the [ "Themes" section](https://themes.gohugo.io/) of Hugo's website. We can create our own theme from scratch if we want, there are several tutorials online about that. For simplicity's sake, for this tutorial, I'll choose the [PaperMod theme](https://github.com/adityatelange/hugo-PaperMod/). To add a theme, you first need to download it as a Git submodule into the `themes/` directory: ```shell git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/papermod ``` Then you need to specify the theme that Hugo should use in the configuration file: ```shell echo "theme = 'papermod'" >> hugo.toml ``` If we restart the development server, we should see a page similar to this: ![Blog homepage with the PaperMod theme](images/01-papermod.jpg "Our blog homepage with the PaperMod theme") Our site is finally starting to look a bit more colorful, but it's still too plain and generic for my taste. ### Customizing the site To make the site less generic, you need to modify the configuration files. For example, we can change the first variables in `hugo.toml`: ```toml baseURL = 'https://example.org/' languageCode = 'en-us' title = 'My New Hugo Site' theme = 'papermod' ``` To thoroughly configure the site, you need to consult the documentation for the chosen theme. [This is the documentation for the PaperMod theme](https://adityatelange.github.io/hugo-PaperMod/). Unfortunately, there is no common documentation for all themes, so you need to take some time and read through your theme's documentation. ### Adding articles Now our blog looks nice, but at the moment, it's a bit empty. We can fix this by creating our first article: ```shell hugo new content posts/.md ``` With this command, Hugo will copy the `archetypes/default.md` file to the `contents/posts/` path, rename it, and substitute the variables: ```toml +++ title = 'Article Name' date = 2024-04-22T00:00:00+02:00 draft = true +++ ``` I would like to point out the `draft = true` variable in our article's preamble: when this is set to `true`, Hugo hides the article during compilation, so you can compile the entire site without worrying about displaying incomplete articles. However, you can still display drafts when running the dev server by passing the `--buildDrafts` argument to the `hugo server` command. Now you can start writing your post using the [Markdown language](https://commonmark.org/): ![A test article](images/02-article.jpg "Here's a test article") Once we've finished writing our articles, we can compile our blog using the command: ```shell hugo ``` The compiled output can be found in the `public/` directory. ## Publishing the site with Cloudflare Pages Now that we have a statically compiled site, we can publish it on a platform like [Cloudflare Pages](https://pages.cloudflare.com/). There are many platforms that allow you to publish static pages, such as [GitHub Pages](https://pages.github.com/) or [Vercel](https://vercel.com/). I chose Cloudflare Pages simply for convenience, since I had previously registered the "nicolabelluti.me" domain with Cloudflare to use [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/), which I'll discuss in a future article[^3]. [^3]: Yes, I know that Cloudflare Tunnel is nothing more than a [MITM](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), and I'll also discuss how to replace Cloudflare Tunnel with your own Wireguard VPN. To create a site with Cloudflare Pages, go to the [Cloudflare Dashboard](https://dash.cloudflare.com/) and navigate to `Workers & Pages`, then click `Pages`. Create a new project, give it a name, and save it without uploading any files. ![This is where you enter the project name](images/03-pages-creation.jpg "This is where you enter the project name") If you go back to the `Workers & Pages` section in the sidebar, you should see the new project. If you want to add your own domain, you can do so in the `Custom domains` section of the project. Make sure to add both the base domain and the `www.` subdomain. Now we are ready to publish the site! To do this, go to the `Deployments` section and click `Upload assets`. ![The "Upload assets" button](images/04-upload-assets.jpg "The \"Upload assets\" button. You can notice some small text under the button...") ### Publishing the site using Wrangler Logging into the Cloudflare Dashboard and manually updating the site every time we need to change something can be a bit... sub-optimal. For this reason, we can use a handy tool to automatically upload all our files from the command line: it's called Wrangler CLI. We can install it via `npm` with: ```shell npm install -g wrangler ``` To use it, we'll need an API key, so Wrangler CLI can update the site on our behalf. To create a key, go to the "[My Profile](https://dash.cloudflare.com/profile)" section of the Cloudflare Dashboard, then navigate to `API Tokens` > `Create Token`, and then `Create Custom Token` at the bottom of the page. The token should have the following parameters: * **Name**: Call it what you like; I'll call it "blog" * **Permissions**: `Account` > `Cloudflare Pages` > `Edit` * **Account Resources**: `Include` > *Your account's email* * **Client IP Address Filtering**: *optional* * **TTL**: *optional, but I **strongly** recommend setting it* ![API Token Parameters](images/05-api-token.jpg "Here are all the parameters you should have set.") Click `Continue to summary` > `Create Token` and save the API token. {{< alert >}} **Save the token in a safe place, it will be shown only this once** {{< /alert >}} Finally, you need to copy the account ID by returning to the [Cloudflare Dashboard](https://dash.cloudflare.com/) and opening your domain. The account ID can be found on the right sidebar, under the "API" section. Now, with the API key and the account ID, we can run Wrangler with: ```shell hugo # You need to compile the site first export CLOUDFLARE_ACCOUNT_ID= export CLOUDFLARE_API_TOKEN= npx wrangler pages deploy 'public/' --project-name= ```