baseUrl and publishDir in Hugo and Cloudflare Pages
TL;DR to use a different base url, in addition to modifying baseUrl, I also had to change publishDir = 'public/ch'. This fixes Cloudflare Pages but breaks the local development version that hugo server runs, so I also made separate config files development and production.
The problem
I wanted my website to be hosted on resmer.co.za/ch so it spells my name at the end. The Hugo docs are pretty clear on how to do this, just set baseUrl in the config to whatever you want the base URL to be.
Unfortunately, when I set baseUrl in the Hugo config and deployed it with Cloudflare Pages, the CSS and JavaScript didn’t load. The stylesheet and script links generated by Hugo were pointing to resmer.co.za/ch/css/ and resmer.co.za/ch/js/, but the actual files were in resmer.co.za/css and resmer.co.za/js. It took a lot of googling and frustration to figure this out, so here is my solution for the next weary traveller.
What is Cloudflare Pages actually doing when it builds a Hugo site?
Cloudflare’s deployment process seems to be:
- build the Hugo site using whatever build command you give it
- copy everything out of the “build output directory” into the root folder of some kind of server. In the copying process Cloudflare totally ignores
baseUrl(and probably all the other Hugo variables).
Hugo’s built-in development server does something automagically to serve the files from /ch/ without actually putting them in a directory called ch. Cloudflare pages doesn’t have any way that I could find to do something similar with the routing, so I need to actually modify what folder that files are being copied into on the server. There is also no setting in Cloudflare to change that directly (other than, like, making the build command hugo && mkdir ch && cp * ch/ or something).
I guess Hugo is assuming that other servers might also do the same thing they do with routing, so baseUrl doesn’t change where the files go, just how the links are constructed. publishDir is what I need!
You still also have to set baseUrl, or the files would be in the ch directory but the links on rendered pages won’t point there. This is confusing because setting both baseUrl and publishDir breaks the local hugo server version (the files end up available at links like resmer.co.za/ch/ch/css/main.css, note the /ch/ch/). I split my config into dev and production so I could still run the local version.
Counterintuitively, I didn’t have to change any of the Cloudflare Pages settings to fix this, but I sure tried. Here’s what they are, and why they don’t work, since they sound so tempting.
-
“Build output directory”: this is the directory Cloudflare will copy wholesale into its server after it’s been built. It really sounds like it would help, but not in this case.
-
“Root directory”: this is the directory Cloudflare will move into before running the build command. Also really sounds promising, but nope, not here.
-
CF_PAGES_URL(environment variable): automatically set to whatever the public URL of your deployment ends up being. The Cloudflare Pages Hugo guide recommends using the build command to set the Hugo baseUrl to the CF_PAGES_URL (hugo --base-url $CF_PAGES_URL). However, I believe setting it in my case would actually break all of my links because it would just set it to the bare domain name, but this may be important in some cases. If I ever need I could probably use"$CF_PAGES_URL"/ch/give or take a/.
Extra stuff
This q and a helped me figure it out (even though the accepted solution is something else): https://discourse.gohugo.io/t/change-output-folder-path-in-public-folder/18636
I’m using Hugo 0.136.3 locally and in production, (set using the HUGO_VERSION environment variable in Cloudflare). Maybe something has changed and if I used the default version Cloudflare suggests it would work correctly, but I wanted to use a newer version than that.