Custom Domain with Cloudflare Pages and Hugo

I host my Hugo blog through Cloudflare Pages as it’s free to host and the deployment process is simple. In this post, I’ll share my experience deploying the blog, the challenges I faced with custom domains, and how I solved them.

Why Cloudflare Pages?

Cloudflare Pages offers an attractive option for hosting static sites due to its simplicity, fast build process, and generous free tier. Deploying is straightforward—you push changes to your repository’s main branch, and Cloudflare automatically detects this, builds the site, and deploys it.

Deployment Basics

Deploying a Hugo site on Cloudflare Pages is straightforward, thanks to the official guide available here. A key piece of information is configuring the baseURL, which Hugo uses to construct full canonical URLs. The recommended build command in the documentation is:

hugo -b "$CF_PAGES_URL/"

The $CF_PAGES_URL environment variable holds the dynamically generated URL for the build. This ensures that URLs for posts and links are accurate for each build. This setup is particularly useful if you push draft posts to a separate branch as each branch gets its own preview URL, preserving the correct link structure.

The Custom Domain Challenge

However, issues arise when using a custom domain for your blog. Hugo’s baseURL configuration must align with the custom domain to generate correct production URLs. The simplest solution is to hard-code your custom domain in the build command:

hugo -b "https://benpoppy.dev/"

This works perfectly for the main branch but breaks preview builds for other branches, as they will also use the custom domain instead of the dynamically generated preview URL.

A Dynamic Solution

To solve this, I discovered the $CF_PAGES_BRANCH environment variable, which specifies the branch being built. Using this variable, we can conditionally set the baseURL in the build command to handle both production and preview builds. Here’s a shell script to achieve this:

if [ "$CF_PAGES_BRANCH" = "main" ]; then
  hugo -b "https://benpoppy.dev/"
else
  hugo -b "$CF_PAGES_URL/"
fi

This script checks if the build is for the main branch. If so, it uses the custom domain; otherwise, it uses the dynamically generated URL. This ensures accurate URLs for both production and preview builds.

Running Locally

When working locally, it’s essential to provide the correct baseURL to ensure consistency with production. The default command for Hugo’s local server can be updated as follows:

hugo serve -b http://localhost:1313/

This ensures that links are generated correctly when previewing your site locally.

Conclusion

By dynamically setting the baseURL based on the branch, you can seamlessly use a custom domain for production while maintaining accurate URLs for preview builds. This setup is robust and allows for a smooth workflow when drafting posts in separate branches.