Automatic cache‑busting with Hugo Pipes: SCSS, JS and fingerprints
Learn how Hugo Pipes compiles SCSS, minifies JavaScript and adds content‑based hashes to filenames so browsers cache assets safely without manual versioning.
01 Jul 2025, 04:59 UTC

Problem: Manual cache‑busting slows down front‑end work
When you change a stylesheet or a JavaScript file, browsers keep the old version cached unless the URL changes. In a static site you have to rename the file or add a query string every time, which is error‑prone and breaks the simplicity of Hugo’s “write once, publish anywhere” workflow.
Thesis: Hugo Pipes gives you automatic, hash‑based fingerprints without extra tooling
Hugo’s asset pipeline (introduced in 0.43 and enhanced with the built‑in esbuild minifier in 0.60) lets you compile, transform and fingerprint assets directly inside Go templates. The resulting file name contains an MD5 hash of its content, so the URL changes only when the asset itself changes.
Section 1: Processing SCSS with a fingerprint
First, place your source stylesheet in assets/scss/main.scss. In the base layout (layouts/_default/baseof.html) add:
{{/* Compile SCSS, minify, then add an 8‑character hash */}}
{{ $css := resources.Get "scss/main.scss" | resources.ToCSS | resources.Minify | resources.Fingerprint }}
The chain works as follows:
resources.Getloads the file from theassets/folder.resources.ToCSSruns libsass to turn SCSS into CSS.resources.Minifyremoves whitespace and comments.resources.Fingerprintcreates a copy namedmain..cssinpublic/assets/and returns a resource object whoseRelPermalinkpoints to that file.
To verify, run the site locally:
- Open a terminal in the site root.
- Run
hugo server(no special permissions needed). - View the page source and look for a
<link>tag whosehrefcontains an eight‑character hex string, e.g.main.a1b2c3d4.css. - Check the
public/assets/directory; you should see a file matching that name.
If you edit assets/scss/main.scss and rebuild, the hash changes and the old file is no longer referenced.
Section 2: Adding JavaScript minification with the same pattern
Hugo 0.60+ ships an esbuild‑based minifier that can also transpile ES6 if you need it. Place a script in assets/js/app.js and process it like this:
{{ $js := resources.Get "js/app.js" | resources.Minify | resources.Fingerprint }}
Because resources.Minify uses esbuild, the output is a single file with a hash‑based name. No external Node or Babel installation is required.
Trade‑off: Build time vs. convenience
Each pipe step adds work to the Hugo build. For a small site the impact is negligible (hugo --timer shows a few extra milliseconds). On larger projects with many SCSS files, multiple PostCSS plugins, or big JavaScript bundles, the pipeline can add seconds to minutes on CI agents with limited CPU.
Practical check: run hugo --timer before adding the pipe, then after adding it, and compare the “Total in” numbers. If the increase hurts your CI budget, consider:
- Limiting the number of files passed through
resources.Get(use globs sparingly). - Moving heavy transpilation to an external build step and only fingerpint the final bundle.
- Using Hugo’s
--minifyflag to defer minification to the publishing step while keeping development builds fast.
Actionable closing
- Ensure you are on Hugo ≥0.60 for the built‑in esbuild JS minifier (run
hugo version). - Add the SCSS and JS snippets shown above to your base layout.
- Start
hugo server, confirm the fingerprinted URLs appear in the HTML. - Run a production build with
hugo --minifyand inspectpublic/assets/to see the hashed files. - Monitor build times with
hugo --timerand adjust the pipeline if the overhead becomes problematic.
With these steps you get true cache‑busting for free, letting browsers keep assets cached as long as they stay unchanged.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.