Measured in your browserWe advise on speed. We practice it.Loaded just now · real numbers from this visit, not a lab score.
Page loaded
First byte
DOM ready
First paint
Largest paint
DNS lookup
TLS handshake
Transferred
Saved by compression
Requests

Cache-Control is the most consequential header most teams half-understand. It is not one instruction but a negotiation between browser caches, CDN caches and your origin, and the parties read it differently. Getting it precisely right is free performance; getting it subtly wrong is a class of production incident that looks like anything but a header problem.

The core vocabulary, precisely

max-age governs every cache including the browser; s-maxage overrides it for shared caches only, which is the lever that lets you run long CDN TTLs with short browser TTLs. no-cache does not mean do not cache: it means revalidate before use. no-store is the actual do-not-cache. private excludes shared caches entirely, and one stray private on a cacheable endpoint silently zeroes your hit ratio for that path. Audit for it; we find it monthly.

The modern extensions

stale-while-revalidate lets a cache serve the expired copy instantly while refreshing in the background, converting revalidation latency to zero for the user who triggers it. stale-if-error keeps serving expired content when your origin is down, which is a resilience feature disguised as a cache directive. immutable tells browsers a versioned asset will never change, eliminating pointless revalidations on reload. Together these three are the difference between a cache that expires and a cache that breathes.

A worked example of the browser-versus-CDN split: Cache-Control: public, max-age=60, s-maxage=86400, stale-while-revalidate=3600 gives browsers one minute of freshness, your CDN a full day, and revalidations that never block a user. The origin sees roughly one fetch per object per day per shield, users see edge latency, and content updates still propagate inside a minute for anyone who refreshes. Five directives, one line, and it outperforms most bespoke caching architectures we audit.

Combinations that misfire

max-age=0 with must-revalidate is a synchronous origin dependency wearing a caching costume. no-cache plus ETag produces a revalidation storm that feels fast on one machine and melts an origin at scale. s-maxage with cookies present may be ignored entirely by CDNs configured to bypass on Cookie. And any directive fight between origin headers and CDN console overrides resolves according to rules each vendor documents differently, so know whose word is final in your stack.

In practice

Define a small taxonomy, three to five content classes with one blessed header recipe each: versioned static (long max-age, immutable), HTML shell (short s-maxage, stale-while-revalidate), API responses (s-maxage seconds, stale-if-error minutes, private where personal). Enforce the recipes in middleware rather than per-route heroics, and diff actual response headers in CI. Header drift is configuration debt with a hit-ratio interest rate.

A header audit is the cheapest engagement we run and routinely the highest-yield. Send ten URLs; we will send back the misfires.

Get the free assessmentMore analysis