We want to cache bust on production deploys, but not waste a bunch of time off the bat figuring out a system for doing so. My thought was to apply a param to the end of css
It is safer to put the version number in the actual filename. This allows multiple versions to exist at once so you can roll out a new version and if any cached HTML pages still exist that are requesting the older version, they will get the version that works with their HTML.
Note, in one of the largest versioned deployments anywhere on the internet, jQuery uses version numbers in the actual filename and it safely allows multiple versions to co-exist without any special server-side logic (each version is just a different file).
This busts the cache once when you deploy new pages and new linked files (which is what you want) and from then on those versions can be effectively cached (which you also want).
It very much depends on quite how robust you want your caching to be. For example, the squid proxy server (and possibly others) defaults to not caching URLs served with a querystring - at least, it did when that article was written. If you don't mind certain use cases causing unnecessary cache misses, then go ahead with query params. But it's very easy to set up a filename-based cache-busting scheme which avoids this problem.
The param ?v=1.123
indicates a query string, and the browser will therefore think it is a new path from, say, ?v=1.0
. Thus causing it to load from file, not from cache. As you want.
And, the browser will assume that the source will stay the same next time you call ?v=1.123
and should cache it with that string. So it will remain cached, however your server is set up, until you move to ?v=1.124
or so on.
Another similar approach is to use htaccess mod_rewrite to ignore part of the path when serving the files. Your never-cached index page references the latest path to the files.
From a development perspective it's as easy as using params for the version number, but it's as robust as the filename approach.
Use the ignored part of the path for the version number, and the server just ignores it and serves the uncached file.
1.2.3/css/styles.css
serves the same file as css/styles.css
since the first directory is stripped and ignored by the htaccess file
<?php
$version = "1.2.3";
?>
<html>
<head>
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
<link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
</head>
<body>
<script src="<?php echo $version ?>/js/main.js"></script>
</body>
</html>
Note that this approach means you need to disable caching of your index page - Using <meta> tags to turn off caching in all browsers?
RewriteEngine On
# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L]
You could take the same approach on any server platform that allows url rewriting
(rewrite condition adapted from mod_rewrite - rewrite directory to query string except /#!/)
... and if you need cache busting for your index page / site entry point, you could always use JavaSript to refresh it.
As others have said, cache busting with a query param is usually considered a Bad Idea (tm), and has been for a long time. It's better to reflect the version in the file name. Html5 Boilerplate recommends against using the query string, among others.
That said, of the recommendations I have seen which cited a source, all seem to take their wisdom from a 2008 article by Steve Souders. His conclusions are based on the behaviour of proxies at the time, and they may or may not be relevant these days. Still, in the absence of more current information, changing the file name is the safe option.
It will bust the cache once, after the client has downloaded the resource every other response will be served from client cache unless: