HTTP Caching, End to EndPhase 2 of 22 min read

Revalidation: ETag and 304

Conditional requests, Not Modified responses, and serving stale content while refreshing in the background.


When a cached response goes stale, the cache doesn't have to download it again. It can ask the server a cheaper question: has this changed?

Validators

A server attaches a validator to a response so caches can ask about it later. There are two kinds.

ETag is an opaque identifier for this exact version of the resource, often a hash of the body:

ETag: "a1b2c3"

Last-Modified is a timestamp:

Last-Modified: Tue, 20 Oct 2026 09:00:00 GMT

ETags are more precise. Timestamps only have one-second resolution and can be wrong after a deploy that touches files without changing them.

Conditional requests and 304

When the cached copy is stale, the browser sends the validator back:

GET /api/profile
If-None-Match: "a1b2c3"

(or If-Modified-Since with the timestamp). The server compares:

  • Unchanged: it replies 304 Not Modified with no body. The cache marks its stored copy fresh again and uses it.
  • Changed: it replies 200 OK with the new body and a new ETag.

A 304 still costs a round trip, but it skips sending the payload, which is the expensive part for large responses.

Weak ETags

ETag: W/"a1b2c3"

The W/ prefix marks a weak validator: the two versions are equivalent in meaning, but not necessarily byte for byte. Servers use this when, for example, compression or whitespace may differ.

stale-while-revalidate

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

For 60 seconds the response is fresh. For the next 300 seconds, a cache may serve the stale copy immediately while it revalidates in the background. The user never waits on the network, and the next visitor gets the refreshed version. It is a good fit for content where being a few minutes behind is fine, like article lists.

Putting both phases together

  • Hashed static assets: max-age=31536000, immutable.
  • HTML pages: no-cache plus an ETag, so every visit is fresh and usually a cheap 304.
  • Per-user API data: private, no-cache with an ETag.
  • Shared, slowly changing content: a short max-age, a longer s-maxage, and stale-while-revalidate.

Check your understanding

Quiz

3 questions. Score 70% or more to complete this phase.

Question 1

Question 1

A browser sends `If-None-Match: "a1b2c3"` and the resource hasn't changed. What should the server return?

Question 2

Question 2

What does the `W/` prefix on an ETag indicate?

Question 3

Question 3

`max-age=60, stale-while-revalidate=300`: what happens 2 minutes after the response?

Results won't be saved unless you log in.