How to Configure Browser Caching and Cache-Busting for Static Assets

Getting caching right involves a balance: cache aggressively enough to reduce repeat downloads, but ensure users always get the latest version after a deployment. This guide covers the standard content-hashing approach that solves both problems simultaneously.

The Core Problem

Long cache times (e.g. one year) mean browsers won't re-download an asset even after you've deployed an update — unless the filename itself changes, giving the browser no reason to think it needs a fresh copy.

The Solution: Content-Hashed Filenames

Modern build tools (Vite, Webpack) automatically generate filenames containing a hash of the file's content:

app.js  →  app-a1b2c3d4.js

When the content changes, the hash changes, producing a new filename — the old cached version becomes irrelevant since nothing references it anymore, and the new file is fetched fresh because it's a filename the browser has never seen.

Nginx Configuration for Hashed Assets

location ~* \.(js|css|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

immutable tells the browser this exact file will never change, letting it skip even the revalidation check normally done on cache expiry.

Never Cache the Entry HTML File Aggressively

location = /index.html {
    add_header Cache-Control "no-cache";
}

index.html references the current build's specific hashed filenames — it must always be fetched fresh (or at least revalidated), or visitors could load an old HTML file referencing assets that no longer exist.

Cache-Busting for Assets Without Content Hashing

If your build process doesn't produce hashed filenames (e.g. hand-written static sites), append a version query string manually:

<link rel="stylesheet" href="/style.css?v=3">

Less robust than true content hashing (requires remembering to bump the version manually), but effective for simple sites without a build pipeline.

Cache Headers for Images

location ~* \.(jpg|jpeg|png|gif|webp|avif|svg)$ {
    expires 30d;
    add_header Cache-Control "public";
}

Images often aren't renamed on every deploy the way JS/CSS bundles are, so a shorter but still substantial cache duration (rather than immutable) is a reasonable default unless you're also hash-naming images.

Cache Headers for API Responses (If Applicable)

location /api/ {
    add_header Cache-Control "no-store";
}

Dynamic API responses generally shouldn't be cached by the browser at all, unless specifically designed to be cacheable.

Understanding Cache-Control Directives

DirectiveMeaning
publicCan be cached by browsers and intermediate caches (CDNs)
privateOnly the browser should cache it, not shared caches
no-cacheMust revalidate with the server before using a cached copy
no-storeNever cache at all
immutableThe resource will never change; skip revalidation entirely
max-age / expiresHow long to cache before requiring revalidation

Verifying Cache Headers

curl -I https://yourdomain.com/assets/app-a1b2c3d4.js

Confirm the Cache-Control header matches what you configured.

Common Errors

Users see a stale version after deployment — verify index.html specifically isn't being cached aggressively, since it's the entry point referencing everything else.

CDN caching interferes with expected behavior — if using a CDN, confirm its own cache settings align with your origin's headers, since CDN-level caching operates independently from browser caching.

Best Practices

  • Use content-hashed filenames for JS/CSS wherever your build tooling supports it
  • Cache hashed assets aggressively (immutable, long expiry)
  • Never aggressively cache the HTML entry point

Continue Reading

Browse more articles in Static Site Hosting & Frontend Deployment.

  • browser caching, cache busting, cache-control, content hashing
  • 0 A felhasználók hasznosnak találták ezt
Hasznosnak találta ezt a választ?

Kapcsolódó cikkek

How to Host a Static Website on a VPS with Nginx

Static websites — plain HTML, CSS, and JavaScript with no server-side processing —...

How to Deploy a Next.js Application on a VPS

Next.js supports several deployment modes — fully static export, server-side rendering with...

How to Deploy a Static Site Built with Astro, Hugo, or Jekyll

Static site generators (Astro, Hugo, Jekyll) produce plain HTML/CSS/JS at build time —...

How to Optimize Images for Web Performance

Images are typically the largest contributor to page weight and load time. This guide covers...

How to Set Up a Jamstack Site with a Headless CMS Backend

The Jamstack architecture (JavaScript, APIs, Markup) combines a pre-built static frontend with a...