Hugo deployment to Cloudflare pages showing style errors
The Problem
I recently wanted to translate Cloudflare's documentation, specifically this one: cloudflare-docs
Their documentation is built with Hugo. After git cloning and deploying directly to Cloudflare, I found the homepage displayed correctly, but other pages had style errors - as if the CSS wasn't loaded.
However, browser debugging showed all CSS was loaded, and comparing with the official website showed loading was fine.

Solution
Using relativeURLs = true
Searching Google for Hugo style error solutions yielded scattered results, but this issue was very similar to my problem: About page styles and path issues
This sentence mentioned there almost made me think I found the solution:

But after adding relativeURLs = true to config.toml and using hugo -D, the opened page had the same problem as when deployed on Cloudflare.
Comparing files generated by npm run dev and hugo -D
I suddenly realized that since npm run dev displays correctly while hugo -D built files show style errors, directly comparing the HTML generated by both would be the best approach!
And this method indeed solved my problem:

Comparing the two, you can see they load style.css differently:
npm run dev
<link rel="stylesheet preconnect" href="../style.css" />
<link rel="stylesheet preconnect" href="../codeBlock.css" />
hugo -D
<link rel="preload" href="../style.css" as="style" />
<link rel="preload" href="../codeBlock.css" as="style" />
Since I'm not familiar with frontend knowledge, I had to ask ChatGPT:

Here's ChatGPT's response:
The main difference between Version A and Version B is how stylesheets are loaded. To understand why Version A works while Version B doesn't, we need to analyze the role of each tag and its behavior in the browser.
Version A
<link rel="apple-touch-icon" sizes="512x512" href="../icons/icon-512x512.png" />
<link rel="stylesheet preconnect" href="../style.css" />
<link rel="stylesheet preconnect" href="../codeBlock.css" />
<link rel="preload" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css" as="style">
Version B
<link rel="apple-touch-icon" sizes="512x512" href="../icons/icon-512x512.png" />
<link rel="preload" href="../style.css" as="style" />
<link rel="preload" href="../codeBlock.css" as="style" />
<link rel="preload" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css" as="style">
Key Difference Analysis
Version A
<link rel="stylesheet preconnect" href="../style.css" />rel="stylesheet"is used to import stylesheets, making the browser immediately load and apply the stylesheet.preconnectis incorrectly used here, aspreconnectis a hint for establishing early connections, but it's invalid in arel="stylesheet"tag.
Version B
<link rel="preload" href="../style.css" as="style" />rel="preload"only hints the browser to preload resources, but doesn't apply them.- An additional
<link rel="stylesheet" href="../style.css" />tag is needed to actually apply the styles.
Why Version A Works While Version B Doesn't
Stylesheet Loading and Application:
In Version A, <link rel="stylesheet" href="../style.css" /> immediately loads and applies the stylesheet. This ensures the page correctly applies these styles when loading.
In Version B, <link rel="preload" href="../style.css" as="style" /> only preloads the stylesheet but doesn't automatically apply it. This means the stylesheet is preloaded but not applied to the page.
Correct Tag Usage:
Version A, despite incorrectly using preconnect, ensures the stylesheet is applied because of rel="stylesheet".
Version B only uses rel="preload", so the stylesheet is preloaded but not applied.
Correct Version B (assuming you want to preload and apply styles) To make Version B work, change it to:
<link rel="apple-touch-icon" sizes="512x512" href="../icons/icon-512x512.png" />
<link rel="preload" href="../style.css" as="style" />
<link rel="preload" href="../codeBlock.css" as="style" />
<link rel="preload" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css" as="style" />
<link rel="stylesheet" href="../style.css" />
<link rel="stylesheet" href="../codeBlock.css" />
<link rel="stylesheet" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css" />
This ensures stylesheets are preloaded and also correctly applied.
Sure enough, it worked immediately after the change.
Finally, asking "Why does Hugo generate incorrect HTML?"
If Version B is automatically generated by Hugo, this could indeed be a Hugo configuration issue or template file settings problem. rel="preload" only preloads resources without applying them. To solve this, you need to ensure the generated HTML contains correct <link rel="stylesheet"> tags so stylesheets can actually be applied.
Solution
-
Check Hugo Configuration and Template Files You need to check your Hugo configuration file (usually
config.tomlorconfig.yaml) and template files to ensure they generate correct HTML tags. -
Modify Hugo Template Files If you're using a custom theme or template, you can manually add correct
<link rel="stylesheet">tags in the template files.
Example: Assuming your template file is at layouts/partials/head.html, make the following modifications:
<!-- Add preload tags -->
<link rel="preload" href="{{ "style.css" | relURL }}" as="style">
<link rel="preload" href="{{ "codeBlock.css" | relURL }}" as="style">
<link rel="preload" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css" as="style">
<!-- Add tags to actually apply styles -->
<link rel="stylesheet" href="{{ "style.css" | relURL }}">
<link rel="stylesheet" href="{{ "codeBlock.css" | relURL }}">
<link rel="stylesheet" href="https://unpkg.com/littlefoot@4.0.0-11/dist/littlefoot.css">
Finally, using global search for rel="preload", I found the CSS loading code location:

A small modification perfectly solved the problem.
{{- if hugo.IsProduction -}}
<!-- In production, preload and actually apply stylesheets -->
<link rel="preload" href="/style.css" as="style" />
<link rel="preload" href="/codeBlock.css" as="style" />
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/codeBlock.css" />
{{- else -}}
<!-- In non-production, directly apply stylesheets -->
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/codeBlock.css" />
{{- end -}}