Skip to main content

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:

ChatGPT

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.
  • preconnect is incorrectly used here, as preconnect is a hint for establishing early connections, but it's invalid in a rel="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?"

ChatGPT

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

  1. Check Hugo Configuration and Template Files You need to check your Hugo configuration file (usually config.toml or config.yaml) and template files to ensure they generate correct HTML tags.

  2. 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 -}}