SysDesignPrep.com
Interactive concepts

HTTP caching with Cache-Control, ETags and fingerprinted assets: an interactive week of visits

Browsers and CDNs cache responses according to the Cache-Control header. max-age lets them reuse a copy without asking for a while; no-cache makes them ask first, and with an ETag the server can answer “not modified” in a few hundred bytes. The best setup for web apps combines both: HTML that is always revalidated, and assets with content hashes in their names that can be cached for a year because a new version always has a new URL.

The playground replays a week of visits, three a day, with a deploy on day 3, under four caching setups. Click any visit to see what the browser fetched.

A page made of 10 files, 1.41 MB in all, visited three times a day for a week; on day 3 at noon a deploy changes the HTML, app.js and styles.css. With no caching, every visit downloads everything: 29.6 MB. A one-day max-age is cheap until the deploy, then for 2 visits the browser runs the new HTML with the old app.js and styles still in its cache: the page is broken until they expire. Revalidating everything is always correct but costs 213 requests, each a round trip, even when nothing changed. Fingerprinted file names cached for a year get both: repeat visits ask only for the HTML, the deploy changes the asset URLs, and the week costs 1.93 MB in 33 requests with no stale visit.

Setup
d1d2d3d4d5d6d7
No cachingno-store
One-day max-ageassets max-age=86400, fixed names
Always revalidateno-cache + ETag on everything
Fingerprinted, immutableapp.3f9a1c.js, max-age=1 year; HTML no-cache
One-day max-age, day 3, 13:00ran old and new files together: 2 requests, 1 download, 0 304s, 30 KB, about 106 ms on a 100 ms round trip.
SetupDownloadedRequestsStale visitsRepeat visit
No caching29.6 MB2100482 ms
One-day max-age1.9 MB872134 ms
Always revalidate2.0 MB2130201 ms
Fingerprinted, immutable1.9 MB330100 ms
downloaded everythingdownloaded changesasked about every file, 304sassets from cachestale mixfirst visit after the deploy

What each setup costs

  • No caching downloads all 1.4 MB on every visit: 30 MB over the week, and every repeat visit waits for the full page.
  • A one-day max-age on assets with fixed names is cheap until a deploy. The new HTML arrives at once, but the browser keeps using its cached app.js and styles.css until they expire, so for the next two visits the page runs new HTML against old code: broken buttons, missing styles, JavaScript errors.
  • Always revalidating with ETags is always correct and nearly free in bytes, 304 responses carry no body, but every visit still makes 10 requests, each a round trip: 213 requests over the week, about 200 ms per repeat visit on a 100 ms round trip.
  • Fingerprinted, immutable assets with revalidated HTML ask only one question per visit. A deploy changes the asset names in the HTML, so the browser fetches exactly the changed files: 33 requests all week, no stale visit, and repeat visits limited by one round trip.

Details that matter

  • no-cache is not no-store. no-cache means “store it, but check before using it”; no-store means never store, for responses with private data.
  • private vs public. Responses for one user should be private, so shared caches and CDNs do not serve them to others.
  • stale-while-revalidate lets a cache serve a slightly old copy while it fetches a fresh one in the background, hiding revalidation latency for content that can be a little stale.
  • Keep old assets around after a deploy for a while: pages already open still reference them.

The headers in practice

# the HTML: always check, so a deploy shows up on the next visit
GET /checkout
Cache-Control: no-cache
ETag: "page-v42"

# next visit: the browser asks "has it changed?"
GET /checkout
If-None-Match: "page-v42"
-> 304 Not Modified          (headers only, no body)

# assets: the name is a hash of the content, so a URL never changes meaning
GET /assets/app.3f9a1c.js
Cache-Control: public, max-age=31536000, immutable
# a deploy ships app.8b20d7.js, referenced by the new HTML; the old file is simply never asked for again

In a system design interview

When a design serves a web or mobile front end, say how it is cached: “Static assets are built with content hashes in their names and served from the CDN with a one-year immutable max-age, so repeat visits load them from the browser cache. HTML and API responses are no-cache with ETags, so a deploy is visible on the next request and unchanged responses cost a 304. User-specific responses are private.” Expect the follow-ups: invalidating the CDN, caching API responses, and what happens to users on an old version.

Read the guide to HTTP caching or CDNs and the edge, see CDN caching, or watch a strong candidate design a URL shortener.

Questions people ask

What is the difference between no-cache and no-store?

no-cache lets the browser store a response but requires it to check with the server, usually with a conditional request, before using it. no-store forbids storing the response at all and is meant for sensitive data.

What is an ETag?

An identifier for a specific version of a resource, sent in the ETag response header. The browser sends it back in If-None-Match; if the resource has not changed, the server answers 304 Not Modified with no body, saving the download but not the round trip.

What does max-age do?

Cache-Control: max-age=N lets a browser or CDN reuse a stored response for N seconds without contacting the server. It removes round trips entirely, but changes to the resource are not seen until the copy expires.

What are fingerprinted or content-hashed assets?

Static files whose names include a hash of their contents, such as app.3f9a1c.js. Because a changed file gets a new name, the old URL can be cached forever with max-age of a year and immutable, while the HTML, which is revalidated, points to the new names after a deploy.

Why do users see a broken page after a deploy?

Typically because new HTML was loaded while old JavaScript or CSS under the same URL was still cached with a max-age, so new markup runs against old code. Content-hashed asset names and no-cache HTML prevent the mismatch.

What does Cache-Control: immutable mean?

It tells the browser that the response will never change during its freshness lifetime, so it should not revalidate it even when the user reloads the page. It is used with content-hashed URLs, which by construction never change.

What is stale-while-revalidate?

A Cache-Control directive that lets a cache serve a stale response for a limited time while it fetches a fresh copy in the background. Users get a fast response and the cache catches up, which suits content that can be slightly out of date.