Using immutable for Versioned Static Assets

Learn how the immutable directive prevents unnecessary revalidation requests for content-hashed static assets, improving reload performance significantly.

Directives

Detailed Explanation

The immutable Directive

immutable tells the browser: this response will never change during its freshness lifetime, so don't bother revalidating even on explicit reload.

The Problem Without immutable

Modern build tools (webpack, Vite, esbuild) generate filenames with content hashes:

app.a1b2c3d4.js
styles.e5f6g7h8.css
logo.i9j0k1l2.png

When the content changes, the filename changes. The old filename literally never changes — it becomes a new file. Despite this, when users press F5 or Ctrl+R, browsers send conditional revalidation requests (If-None-Match) for every cached resource, even with a long max-age.

These requests always return 304 Not Modified because the content hasn't changed. They waste bandwidth and add latency to page reloads.

The Solution

Cache-Control: public, max-age=31536000, immutable

With immutable, the browser skips revalidation entirely on reload. The resource is served from cache instantly — zero network requests.

Performance Impact

For a typical SPA with 20+ static assets:

  • Without immutable: Reload sends 20+ conditional requests (even if all return 304)
  • With immutable: Reload serves all from cache with zero network activity

The improvement is most noticeable on high-latency connections (mobile networks, distant servers).

Browser Support

immutable is supported by Firefox (since v49) and Safari (since v11). Chrome does not support it but has its own heuristic that achieves similar behavior for resources with very long max-age values. Including it is still recommended — browsers that don't recognize it simply ignore it.

When NOT to Use immutable

Never use immutable on resources without content hashes in their filenames. If /styles.css changes but keeps the same URL, immutable will cause browsers to serve the old version until max-age expires.

Use Case

A React application built with Vite outputs files like 'index-Bk3d9f2a.js' and 'index-H8x4n1p.css'. Setting 'Cache-Control: public, max-age=31536000, immutable' on these assets means users never re-download them unless the content actually changes (which produces a new filename). On a mobile connection with 200ms latency, skipping 20 conditional requests saves 4 seconds on page reload.

Try It — Cache-Control Builder

Open full tool