Tags: web-dev concept

HTTP Caching

Date: 2026-08-16


Telling the browser it may reuse a response instead of asking again. The whole design is a trade between freshness and round trips — and the standard resolution is to cache aggressively and change the filename when the content changes.


What it is

HTTP caching is a set of response headers instructing browsers and intermediaries how long a response may be reused, and how to check whether it’s still valid.

Two distinct mechanisms, and knowing which one you’re using matters:

  • Freshness — the response may be used with no request at all until it expires
  • Validation — the response has expired, so the browser asks “has this changed?” and may get a cheap 304 Not Modified

Freshness saves an entire round trip. Validation saves the download but still costs the round trip — which on mobile is most of the cost.

Cache-Control

Cache-Control: public, max-age=31536000, immutable
Cache-Control: private, max-age=0, must-revalidate
Cache-Control: no-store
DirectiveMeans
max-age=NFresh for N seconds
s-maxage=NSame, but for shared caches (CDN) only — overrides max-age there
publicShared caches may store it
privateBrowser only. Use for anything user-specific
no-cacheStore it, but always revalidate before use. Not “don’t cache”
no-storeNever store. The actual “don’t cache”
immutableWon’t change before it expires — skip revalidation even on reload
stale-while-revalidate=NServe stale for up to N seconds while refreshing in the background

no-cache is the most misread directive in HTTP. It permits caching and requires revalidation. no-store is the one that means what people think no-cache means.

The standard pattern

/assets/app.4f9a2b1c.js     Cache-Control: public, max-age=31536000, immutable
/assets/main.7d3e8a45.css   Cache-Control: public, max-age=31536000, immutable
/product/wool-socks         Cache-Control: public, max-age=0, s-maxage=300,
                                           stale-while-revalidate=86400
/account                    Cache-Control: private, no-store

Fingerprinted assets cached for a year. The hash in the filename changes when the content does, so a new deploy produces a new URL and there is no invalidation problem — the old file simply stops being requested. immutable stops the browser revalidating even on a manual reload.

HTML cached briefly at the CDN, not in the browser. max-age=0 means the browser always checks; s-maxage=300 lets the CDN serve it for five minutes without touching the origin. That combination gives you fast TTFB and quick content updates at once — Time to First Byte, CDN Caching.

Personal pages not stored at all. An account or basket page in a shared cache is a data breach.

stale-while-revalidate

The most useful directive people don’t use:

Cache-Control: max-age=60, stale-while-revalidate=86400

For 60 seconds the response is fresh. For the next 24 hours it’s served immediately from cache while being refreshed in the background. The user never waits for revalidation; the content is at most slightly stale.

Ideal for content that changes but where a minute-old version is harmless — category pages, navigation, content blocks.

Validators

When freshness expires:

ETag: "a4f2c9"                            strong content identifier
Last-Modified: Wed, 13 Aug 2026 09:14:00 GMT

The browser sends If-None-Match or If-Modified-Since; the server answers 304 with no body if unchanged. ETags are more precise; Last-Modified has one-second resolution.

Watch ETags behind multiple servers — if each generates a different ETag for identical content, every request is a cache miss. This is a classic load-balanced deployment bug and it silently disables caching.

What it does to your data

Caching changes what you can measure, which matters when analysing performance:

  • Warm visits are much faster than cold ones, so a bimodal distribution in Real User Monitoring usually means cache hit versus miss, not two kinds of user — Percentiles in Performance
  • Lab tests are cold by default and therefore pessimistic against a returning-visitor population — Field vs Lab Data
  • Cache hit rate is a first-class metric. A CDN at 60% hit rate means 40% of users are waiting on your origin

Traps

  • Caching HTML in the browser with a long max-age. Users get a stale page and clearing it requires them to hard-refresh, which they won’t
  • no-cache intended as no-store, leaving private data in a shared cache
  • Forgetting Vary. A response varying by Accept-Encoding, Accept or cookie must say so, or a cache serves the wrong variant to the wrong user
  • Unfingerprinted assets with long expiry. Now you have an invalidation problem, and purging a CDN is not the same as purging a million browsers
  • Cookies defeating CDN caching. Many CDNs skip caching for requests carrying cookies. A single analytics cookie on a static asset path can disable the whole layer