How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

Cache-bust an asset URL

Append the deployed commit to every built asset URL with getLastVcsHash(); it changes on every deploy. Uploaded files use the change-time setting, and replaced images clear their own cache.

Context

Context. Built assets are served with a long cache lifetime, so a returning browser keeps its own copy of app.css until the URL changes. The platform gives every deploy a hash, getLastVcsHash() on the site, that changes with every push. Put it in a ?v= parameter and the URL changes exactly when the file can. Uploaded files are a different case: a file replaced in the file manager keeps its address on purpose, so those URLs are busted by a setting rather than a template.

Steps

  1. Add ?v= and the hash to every stylesheet and script URL in the base template.
  2. Pass the hash to your JavaScript in a <meta> tag, so anything a script loads itself (a sprite, a chunk, a stylesheet) can carry it too.
  3. For uploaded files behind a third-party CDN or proxy, turn on the Add change time parameter to URL setting, which appends a value derived from the file's last change.
  4. Do nothing for transformed images: when a file is replaced, the platform clears its cached versions itself.

Code

Template code
{$v = $_currentSite->getLastVcsHash()}
<link rel="stylesheet" href="/dist/app.css?v={$v}">
<meta name="asset-version" content="{$v}">
<script nonce="{$_inlineScriptNonce}" src="/dist/app.js?v={$v}" defer></script>
{% set v = _currentSite.lastVcsHash %}
<link rel="stylesheet" href="/dist/app.css?v={{ v }}">
<meta name="asset-version" content="{{ v }}">
<script nonce="{{ _inlineScriptNonce }}" src="/dist/app.js?v={{ v }}" defer></script>
JavaScript
// in the bundle: load a sprite with the same version
var v = document.querySelector('meta[name="asset-version"]').content;
fetch('/dist/icons.svg?v=' + v);

Why this and not that

Not a timestamp: it changes on every request and defeats caching altogether. Not a version bumped by hand: it is forgotten, and then a deploy ships old CSS to every returning visitor. Not a hash in the file name: the platform serves the committed file at its committed path, so a renamed file needs every reference updated on every deploy. A repository that serves several sites from one codebase uses getAnySiteLastVcsHash(), which finds a hash on any environment of the same name.

Go deeper

Was this page helpful?

Your answer goes to the docs team.