Long cache lifetimes without stale files: cache busting explained
You fix a broken layout, upload the new stylesheet, refresh the page and everything looks right. Then a customer calls to say the page is still broken on their phone. Their browser is holding an older copy of the file and has no reason to ask for a new one. This is the classic trade-off with browser caching: long cache lifetimes make repeat visits fast, but they also keep outdated files alive. Here is cache busting explained in practical terms, so you can cache aggressively and still ship changes that every visitor receives straight away.
Why long cache lifetimes and updates clash
When your server sends a file with a header such as Cache-Control: max-age=31536000, it is telling the browser that the file can be reused for a year without checking back. For a logo that never changes, that is ideal. For a stylesheet you edit every month, it becomes a problem, because the browser trusts that promise and will not request style.css again until the year is up.
Many site owners respond by shortening the lifetime to a few minutes or disabling caching altogether. That avoids stale files, but every page view now triggers extra requests, and visitors on patchy mobile data pay for it with slower pages. There is a better approach.
The core idea: change the name, not the file
Browsers cache by URL. If the URL changes, the browser treats it as a brand-new file and downloads it, no matter what is stored for the old address. Cache busting uses this rule deliberately. Instead of overwriting style.css, you publish the updated file under a new name and update the HTML to point at it.
The old file can keep its one-year cache lifetime because nothing references it any more. The new file also gets a one-year lifetime, which is safe because its content will never change under that name. Every future edit produces yet another name.
Three common ways to version a file
- Query string: style.css?v=12. Easy to add by hand and widely used in CMS themes. Most browsers and CDNs handle it correctly, though some proxy and CDN setups ignore query strings when caching, so check your configuration.
- Version in the file name: style.v12.css. More reliable across caches, but someone has to remember to bump the number.
- Content hash in the file name: style.3f9a1c.css. The hash is calculated from the file contents, so it changes only when the content changes. This is the most dependable method and needs no human memory.
Let your build tool hash assets for you
Manually renaming files works for a tiny site, but it breaks down quickly. Modern front-end build tools do the job automatically. Vite, webpack and Laravel Mix can all output files with a content hash in the name and generate a manifest, a small JSON file that maps the original name to the hashed one.
Your templates then read the manifest rather than hard-coding file names. In Laravel, for example, the Vite helper inserts the correct hashed path into the page. WordPress themes often append a version number through the enqueue functions, and many caching plugins can add versions for you.
A useful side effect of hashing is that files which did not change keep their old names. If you only edited the stylesheet, the JavaScript bundle keeps its hash, and returning visitors do not download it again.
Cache HTML differently from CSS and JavaScript
Cache busting only works if the HTML page itself is fresh. The page is where the new file names live. If the browser serves a cached copy of the HTML, it will keep requesting the old stylesheet name and never learn about the new one.
A sensible default policy looks like this:
- Hashed CSS, JS, fonts and images: Cache-Control: public, max-age=31536000, immutable. The immutable directive tells supporting browsers not to revalidate even when the user reloads.
- HTML pages: Cache-Control: no-cache, which means the browser may store the page but must check with the server before using it. With an ETag or Last-Modified header, an unchanged page returns a tiny 304 Not Modified response.
- Un-hashed files you cannot rename, such as favicon.ico or robots.txt: a short lifetime of a few hours to a day.
Note that no-cache does not mean "do not store". The directive that forbids storage is no-store, which suits pages with personal data such as account screens or order details.
Deploying safely without broken pages
Even with hashed names, the order of a deployment matters. If you delete the old assets the moment the new version goes live, a visitor who loaded the old HTML a few seconds earlier may request a stylesheet that no longer exists and see an unstyled page.
- Build the new assets and upload them alongside the existing ones.
- Switch the HTML templates or manifest to reference the new names.
- Purge the HTML from any CDN or page cache, but leave hashed assets alone.
- Keep the previous release’s assets for at least a few days before cleaning up.
- Open the site in a private window and on a phone to confirm the new files load.
If you use a server-side page cache or a CDN that caches HTML, step three is essential. Otherwise the CDN keeps handing out old HTML that points to old files, and your cache busting has no effect.
Checking that your setup actually works
Open Chrome DevTools, go to the Network tab and reload the page twice. On the second load, hashed assets should show "memory cache" or "disk cache" in the Size column, while the HTML document should show a request, often returning 304. Click a stylesheet and read its response headers to confirm the long max-age.
Lighthouse also flags static assets with short cache lifetimes under its caching audit. If it lists your main CSS and JS files, either they are not versioned yet or the server is not sending long cache headers for them.
Putting a cache-busting policy in place this week
Start by listing how your site currently references CSS and JavaScript. If file names never change between releases, add versioning first, ideally through your build tool. Then set long lifetimes for versioned assets and revalidation for HTML, and write the safe deploy order into your release notes so nobody skips it.
If your hosting stack makes header changes awkward, our team can configure it as part of managed Linux hosting or a wider performance review during website development.
Frequently asked questions
Is a query string like ?v=2 enough for cache busting?
For most small sites it works. The weakness is that it relies on someone updating the number and on every cache layer respecting query strings. Hash-based file names avoid both issues.
Why do I still see old styles after clearing my browser cache?
Another layer is probably serving the old file, such as a CDN, a hosting page cache or a WordPress caching plugin. Purge those too, then confirm the HTML references the new file name.
Does the immutable directive work in every browser?
Not all browsers honour it, but it is harmless where unsupported. Those browsers simply fall back to the max-age value, which is already long for versioned files.
Should images get hashed names as well?
Images processed by your build tool can be hashed like CSS. Images uploaded through a CMS usually get unique names on upload, so a long lifetime is safe as long as editors upload replacements rather than overwriting files.
We can do this for you
See what a package covers and what it costs, or ask us about your own project.

