Cache-Control for a Content Site, Explained by What Breaks
IETF RFC 9111, published on June 6, 2022, established the baseline rules that govern modern web caching
Last reviewed
IETF RFC 9111, published on June 6, 2022, established the baseline rules that govern modern web caching: an instruction sent in a response header determines whether an intermediary or a user device holds onto a file or throws it away. When those headers are misconfigured on an HTML page, bad data sits stranded on thousands of consumer laptops, impervious to origin server updates until a timer expires.
Configuring cache-control headers on a content site comes down to managing the differences between response types. Static assets with unique hashes in their filenames can live in client storage for months. HTML documents, by contrast, carry the URLs that point to those assets. Caching HTML too aggressively breaks the deployment pipeline, locking visitors into obsolete versions of an entire website.
Cache-Control: public, max-age=31536000, immutable
Fingerprinted Assets: Cache Hard, Change the URL
Static assets that include a build hash or version token directly in the filename, such as app-d41d8cd98f.js or styles.e2fc714c.css, never mutate under that specific path. Because any code edit produces a new filename during compilation, the existing file path remains permanently valid for the exact payload it delivered on day one.
For these resources, MDN documentation notes that the immutable directive tells client software that the response will not change throughout its freshness lifetime. Under standard conditions, a browser that reloads a page might issue conditional requests to check if an asset changed. The addition of immutable directs the browser to skip sending conditional revalidation requests while the resource remains unexpired.
Combining a large max-age value with immutable removes repetitive network overhead. Cloudflare documentation updated on June 30, 2026, details that directives like stale-while-revalidate also allow intermediate caches to serve an expired response for an extra duration in seconds while initiating a background fetch to the origin. On fingerprinted assets, however, updates do not happen in place. A long freshness duration paired with immutable ensures that the client never wastes round trips querying the origin about static binary files whose underlying bits cannot change without changing their name.
The failure mode here is failing to mark immutable assets correctly. Omitting these instructions leads to millions of redundant network checks for files that were finished the moment they were compiled.
Unfingerprinted Assets: Preserving the Revalidation Loop
Some static files cannot easily carry content hashes in their paths. Canonical logos, favicon.ico, robots.txt, and fixed-path vector illustrations must live at static URLs so that third-party services and legacy links continue to resolve.
These resources require strict revalidation discipline. A common misunderstanding in web development is that the no-cache directive blocks caches from saving data. It does not. MDN documentation clarifies, that no-cache allows a response to be saved in storage, but explicitly prevents the cache from reusing that stored response without validating it against the origin first.
Cache-Control: no-cache
When an origin serves an asset with no-cache, the browser or proxy stores the bytes alongside a validator, such as an ETag or a Last-Modified timestamp. On subsequent visits, the client sends a conditional request back to the server. If the asset has not changed, the server answers with a lightweight 304 Not Modified status and an empty body. The client reuses its local copy immediately.
Directives like must-revalidate behave differently. MDN documentation states that must-revalidate permits an asset to be reused freely without network requests while it remains within its freshness window. Once that freshness period expires, the cache is strictly forbidden from serving the stale copy without verifying it at the origin. Cloudflare documentation confirms that both must-revalidate and no-cache prevent edge systems from serving stale content when origin cache control features are activated.
For sites that need immediate updates without sacrificing local storage, web.dev provides an example header for reusable assets on December 11, 2020:
Cache-Control: max-age=0, must-revalidate, public
This pattern guarantees that zero stale responses reach the visitor, while saving bandwidth via conditional checks whenever the asset remains unchanged.
HTML Documents: The Dangerous Response
HTML documents are the entry point of the application graph. They dictate which stylesheets, scripts, and media files the browser needs to load. Assigning a long max-age directly to an HTML file is one of the most destructive configuration errors an engineering team can commit.
If an HTML page is cached locally by an end-user browser for seven days, that browser will not contact the origin server for seven days. If a developer deploys a critical fix two hours later—updating the HTML to point to new fingerprinted script bundles—the visitor with the cached HTML never sees the update. Their browser continues to execute the old HTML, which attempts to load old script dependencies. If the origin server or content delivery network has cleared out earlier build artifacts, the old HTML calls missing files, triggering catastrophic application errors.
You cannot remotely invalidate a standard browser cache once a long freshness lifetime is handed out. While a content delivery network allows cache purging via administrative interfaces or API calls, private caches running inside thousands of distinct client browsers remain completely isolated from server-side eviction events.
Because of this asymmetry, root HTML documents must require origin validation. Serving HTML with no-cache or max-age=0, must-revalidate forces the client to verify that the HTML representation is up to date before rendering. If the document has not changed, the origin issues a fast 304 Not Modified. If the content was edited, the visitor receives the new markup immediately, preserving the integrity of downstream asset references.
Where absolute data privacy or immediate exclusion from disk is required, RFC 9111 defines no-store. Summarised by cache-control.com on January 15, 2024, no-store instructs all caching mechanisms that they must not store any part of either the request or the response on persistent storage or shared memory.
Managing Shared Proxies with s-maxage and Vary
Content delivery networks and corporate proxies act as shared caches, sitting directly between the origin server and multiple distinct users. To manage these intermediaries independently from end-user devices, RFC 9111 provides the s-maxage directive.
The s-maxage directive applies strictly to shared caches, overriding both max-age and legacy Expires headers. When an origin emits:
Cache-Control: public, max-age=0, s-maxage=86400, must-revalidate
The browser receives a freshness lifetime of zero, prompting it to validate before rendering. The edge CDN, meanwhile, retains the page for up to 86,400 seconds (one full day), shielding the origin database from redundant rendering loads. Because edge caches support instant API-driven purges, site operators can maintain high edge cache hit rates without losing the ability to flush bad HTML within seconds of a broken deploy. RFC 9111 mandates that a shared cache must not reuse a stale response carrying s-maxage until the origin confirms the payload.
Vary: Accept-Encoding, User-Agent
Edge caching behavior is further complicated by content negotiation. RFC 9111 dictates that when a response includes a Vary header, the cache must not reuse that stored representation unless the nominated request headers match the original incoming request.
If an origin sends Vary: Accept-Encoding, an edge proxy creates separate cache buckets for requests requesting gzip, Brotli, or uncompressed bodies. If headers that produce hundreds of unique variations across users are included in Vary, the cache fragments into tiny buckets, driving down the overall hit ratio and forcing the origin to process duplicate workloads.
The operational rule for content architectures remains firm: push long cache lifetimes to fingerprinted assets where URLs change upon mutation, keep HTML attached to a revalidation requirement, and let s-maxage isolate edge delivery from unmanageable browser storage.