Cache busting of CSS/JS

I’m not sure if this is out of scope for Hugo, but I thought I’d mention it here to get some input.

As a way to being able to use far future expire headers on CSS and JS files, I’ve been using a method of adding a date to the filename, like: style.20141003.csswhich has served me well. It’s easy to set up a rewrite on the server to just route it to style.css.

Since a rewrite like that isn’t possible on a static server such as S3 (as far as I know at least), I was thinking of a way to solve it on Hugo generation instead, but haven’t been able to figure out a way. It would basically need to rename the file on copy.

The most straight forward solution would be to use the query string way of style.css?v123, but the last time I checked it wasn’t recommended due to issues with some proxies. That way should be possible to do quite easily right now with Hugo though.

Any other ideas?

1 Like

This seems to work well:

<link rel="stylesheet" href="/static/css/style.css?{{ .Now.Unix }}">

Probably related to this topic - but in the current situation it’s not uncommon to combine Hugo with build tools like Grunt or Gulp, which have plenty of tools for this (I use a plugin that put a content MD5 hash into the filenames).

Yeah, you’re right. I probably need to finally learn one of those systems.

I do eventually want to add some of these features into Hugo, but only when it makes sense. These other tools do a great job already. I think it’s only good to add to hugo when no configuration is needed. As soon as we add a feature that requires a bunch of extra configuration then I feel they are better served in a separate program.

2 Likes

This stuff just makes me sad. Cache busting resource URLs kills innocent http puppies. Cache should be a server configuration issue not something for HTML link/script tags.

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?