Table of Contents

Introduction

I’ve been using Cloudflare Pages for a while now, after migrating from GitHub pages. The limits are more generous, the preview deployments are super handy, and I was already using Cloudflare.

After recently redoing my homepage, I realized that a lot of links and images would be wrong when making a pull request. They were pointing to the live site rather than the preview deployment for that pull request.

Problem

The problem lies with the Hugo baseURL setting. When absolute URLs are created in templates, the path is appended to the end of the base URL. As Cloudflare Pages uses a new domain name for every deployment, this is not a static value.

Solution

While I would love to just set the base URL to be “/” and be done, some places like the contents of the sitemap or canonical URL MUST be an absolute URL (https://example.com/page rather than /page).

My first recommendation is to convert as many links as possible to relative URLs rather than absolute URLs. This solves most of the problems. If you’re using a third party theme, you may be able to create some patches to help. I like this package a lot. More about that here.

What I also ended up doing was to leave the baseURL setting to “/”, so when developing locally everything worked. I then made a quick script that checked if the build was running on a deployment build, or the main branch. If running on a deployment build, it would use the value from the CF_PAGES_URL environment variable, which gives the full URL of the deployment. If the build is running on the main branch, I hardcoded the URL, since the CF_PAGES_URL environment variable always gives the <commit>.<project>.pages.dev domain, even if you have a custom domain setup.

This culminates to this:

package.json

 1{
 2  "dependencies": {
 3    "cross-spawn": "^7.0.3",
 4    "hugo-extended": "^0.111.3"
 5  },
 6  "scripts": {
 7    "build": "node build.js",
 8    "postinstall": "patch-package"
 9  }
10}

build.js

 1const spawn = require("cross-spawn");
 2const fs = require("fs");
 3var base_url = "/";
 4
 5if (process.env.CF_PAGES_BRANCH === "main") {
 6  base_url = "https://blog.nathanv.me/";
 7} else if (process.env.CF_PAGES_URL) {
 8  base_url = process.env.CF_PAGES_URL;
 9}
10
11console.log(`Using base url "${base_url}"`);
12cmd = spawn.sync(
13  "hugo",
14  ["--cleanDestinationDir", "--minify", "-b", base_url],
15  { encoding: "utf8" }
16);
17
18if (cmd.error) {
19  console.log("ERROR: ", cmd.error);
20}
21
22console.log(cmd.stdout);
23console.error(cmd.stderr);
24
25process.exit(cmd.status);

While not pretty, it works mostly. If you have more than one custom domain (like a www version), that one will just exclusively point to the custom domain you choose as “most” canonical.

EnvironmentWorks
Latest Main (custom domain)Yes
Alternative Latest Main (www.custom domain)Kinda
Older Main (<commit>.<project>.pages.dev)No
Preview (<commit>.<project>.pages.dev)Yes
LocallyYes