Caching

Validators and conditional requests

Revalidate stale responses with ETag or Last-Modified and understand why a 304 response can save a full body download.

When a cached response goes stale, the client does not always need to download the full body again. It can ask the server: “has this changed?”

An ETag identifies a version of a representation:

ETag: "article-v5"

The next request sends that value back:

If-None-Match: "article-v5"

If nothing changed, the server returns 304 Not Modified with an empty body. The client reuses its stored copy. You save bandwidth and parsing time even though the request still reached the server.

Last-Modified and If-Modified-Since work the same way with a timestamp. ETags can be more precise because the server controls how it names each version.

Let’s walk through it with curl. First, fetch headers and save the ETag:

curl -I https://flaviocopes.com/img/og.png

Note the etag line in the output, something like etag: "abc123...". Send it back:

curl -I -H 'If-None-Match: "paste-the-etag-here"' https://flaviocopes.com/img/og.png

If the file has not changed, you get HTTP/2 304 and no body. curl prints the status line and headers only.

Freshness (max-age) avoids a network round trip entirely while the response is still fresh. Validation still contacts the server, but skips transferring and parsing a large body. For a 2 MB JavaScript bundle, a 304 is a big win.

Be careful generating ETags. If your server emits a new ETag on every request even when the content is identical, caches never get a 304. They refetch the full body every time. Stable ETags require stable content or a hash of the content.

My advice: use validators on anything large enough to hurt when it changes rarely. Images, fonts, and JS bundles are the usual candidates.

Lesson completed