Cache-busting bundles with assetUrl
What you'll see
A developer publishes an updated JS or CSS bundle, but visitors keep running the old version — sometimes for up to a day — because the bundle URL never changed and the browser saw no reason to refetch it. The symptom is not just wrong styling: an old JS bundle running against new HTML causes functional breakage, and it clears itself only when the browser cache expires or the user hard-refreshes.
What's actually happening
Browsers cache static assets aggressively. If the URL of a bundle stays the same across a publish, the browser treats the cached copy as current and does not refetch it. docly.assetUrl(path) solves this by appending ?v=<version> to the path, where the version is the site's last-modified timestamp formatted yyyyMMddHHmmss. The stamp is site-wide (not per file) and changes on every publish.
The server backs this up: any response whose URL carries a ?v= query is served with Cache-Control: max-age=31536000, immutable (one year). So an unchanged bundle is never refetched unnecessarily, and a changed bundle gets a brand-new URL after a publish and is refetched immediately. Public HTML without ?v= is served no-cache instead, so it revalidates cheaply (a 304 via Last-Modified). External URLs (containing :// or starting with //) are returned unchanged, with no ?v=.
What to do
Wrap all of your own <script src> and <link href> references in docly.assetUrl(...):
<script src="#docly.assetUrl('/assets/js/bundle.js')#"></script>
<link href="#docly.assetUrl('/assets/css/site.css')#" rel="stylesheet">The bundle now gets ?v=<version>. A new publish means a new version, a new URL, and an immediate refetch by the browser. The server sets immutable, max-age=1 year on ?v= URLs, so unchanged bundles are never refetched unnecessarily — this also scores full marks on Google's "efficient cache policy" audit.
⚠ Does NOT work for your own dynamic output. assetUrl versions by publish time (the site's last-modified timestamp), not by the actual content of a dynamically generated response. So do not use assetUrl on a URL that produces content which varies without a publish:
- output that depends on query parameters, the logged-in user, the current time, counters, or external sources
- anything that must be fresh per request
Why it is dangerous: a ?v= URL gets immutable, max-age=1 year. If the browser or CDN caches the first dynamic response, that same response is served to everyone for a whole year — stale content, and potentially one user's data leaking to others.
What to do instead: serve dynamic or per-user content from a .js API endpoint under #/API/. API responses are no-cache by default and must not be versioned with assetUrl. Fetch them from the browser at runtime.
Rule of thumb:
- Static bundle/asset (changes only on publish) → use assetUrl.
- Dynamic / per-user / per-request output → do not — serve it as a
.jsAPI (no-cache). - External URL (CDN etc.) → no — assetUrl leaves it unchanged anyway.