Cache busting of CSS/JS

Of course this breaks the beauty of Hugo’s single binary install. But for ,

I agree that caching should be configured on the server, but cache busting is useful when we site updates available quickly. Also cache busting allows for longer TTLs on content, which helps

@bep is there a way for me to “inject” data into a hugo build session? I have looked, and did not find anything. I am thinking my build tool (make) could generate a build number, which it would use to name css/js resources. I just don’t see a way to get the build number into the hugo templates. If it could be injected into hugo as a key-value pair, and be available in .Site or a new sub variable in .Site.

There an os env var templ func.

That could do the trick. I will try it out. Thank you.

@UkiahSmith, if you work out something elegant, I’m sure the community would like to see it.

I worked up a way to use getenv and make to automate css names for cache busting: https://ukiahsmith.com/blog/hugo-static-asset-cache-busting/

It generates a random number in make, puts it in the environment. Then a Hugo templates uses getenv to get the number, and puts it in the css name.

2 Likes

I wrote a small Go program to generate the sequence numbers, based on system time. See https://bitbucket.org/rickb777/timestamp

Then I run a shell script: make-config.sh

#!/bin/bash -e
cd $(dirname $0)
OUT=${1:-data/build.yaml}

timestamp=$(timestamp -base 36 -precision min -zero 2015-07-01)
echo "timestamp=$timestamp" | tee timestamp.cfg

mkdir -p $(dirname $OUT)
echo "timestamp: $timestamp" > $OUT

Then I use .Site.Data.build.timestamp in my template partials, e.g.

This presumes there is a directory with name starting with a and containing the timestamp. This is achieved by putting the static files that are ‘far-future’ into static/a and making a symlink like this:

. timestamp.cfg
cd htdocs
ln -sv a a$timestamp

(which is also done in a script).

1 Like

A little crazy idea I had yesterday:

<link href="/layout.css?{{ slicestr (readFile "static/layout.css" | md5) 0 10 }}" rel="stylesheet">
1 Like

That looks ineffective. Another way would be to append .Now.Unix (epoch time) or something.

To append query strings isn’t proxy proof, indeed. Some proxies bypass requests using query strings.

To append epoch time is worst. It forces to download files that haven’t change.

My remark about the ineffectiveness was about the file reading and md5 calculation for every HTML file generated.

You’re right! I’m sorry.

In case Hugo gets a feature for cache busting (what I believe it should), the md5 needs caching until the last HTML be generated.

1 Like

Agree. Cache busting feature would be nice. I’m using {{ now.Unix }} for now.

This seems like a very good solution to me. Simple and effective. Why would you want anything else?

Probably mentioned it elsewhere but I’m doing this with the time and the git hash on deploy. Deploying with a zsh function which gets the hashes as needed:

Hugo builds accessing the env vars or the time:

I updated the method which I was generating unique filenames for CSS assets. I wrote a script to process my Sass files, and append a git hash for the commit to the filename.

The script I use.

From my experience with writing my current solution I am not sure if cache busting should be part of Hugo itself. There are several ways in which to create unique file names; Hugo can’t support them all, and choosing one would be rude to the others.

I do think there could be improvements to how Hugo links to external assets. Maybe some interface an external script/tool could adhere to; similar to how I use the JSON data file.

1 Like

I followed the blog post by Carl Johnson on setting up hugo asset pipeline. That makes use of a Go commandline tool called scattered developed by the author to do the cache busting.

I am not a web developer so Gulp/Grunt/etc is very foreign to me. So I followed his post and developed a poor man’s asset pipeline using a simple shell script wrapper. I might convert that shell script to a Makefile in future.

1 Like

really helpful, thanks!

Have you seen: https://gohugo.io/hugo-pipes/

I have a solution that appends a query string that only changes when the file is modified.

For my theme css file:

{{ $stylemin := os.Stat "themes/coder/static/css/style.min.css" }}
<link rel="stylesheet" href="{{ "css/style.min.css" | absURL }}?{{ $stylemin.ModTime.Unix }}">

and for any custom_css files in config.toml:

{{ range .Site.Params.custom_css }}
  {{ $stylecustom := os.Stat (print "static/" . ) }}
  <link rel="stylesheet" href="{{ . | absURL }}?{{ $stylecustom.ModTime.Unix }}">
{{ end }}

I have a really small site, so I’m not sure of the performance effect on building a larger site. Hopefully it is faster than reading and hashing the file.

EDIT: Hugo Pipes can do this now with the fingerprint option. When used, it changes the $style.Permalink to, for example, style.{HASH}.css

4 Likes