Skip to content
All writing ‹ Part 04 of 18 · A Cache Is a Chain, Not a Box
Engineering · 5 min read

Cache Freshness: Fresh, Stale, and 304

How an HTTP cache decides a copy is fresh or stale with max-age and Age, how a 304 refresh works, and why a cache is only as fresh as the copy it receives.

A cache keeps a copy, so the next request does not need to reach the server. In backend terms, it is a memo cache in front of a slow function.

The hardest part of a memo cache is deciding when to drop the old value. An HTTP cache uses two numbers to decide: max-age and Age.

browser CDN cache (only when needed) origin hit: a fresh copy is here, answer in a few milliseconds miss: no copy, or the copy is too old, ask the origin
A hit answers from the CDN cache in milliseconds, and only a miss goes on to the origin.

max-age and Age

max-age = 3600 s fresh stale age: 54 0 s 3600 s fresh time left = 3600 - 54 = 3546 s
A copy is fresh until its age reaches max-age, so the time left is max-age minus age.
  • cache-control: public, max-age=3600: any cache can keep this copy for 3600 seconds.
  • age: 54: this copy has already sat in a cache for 54 seconds.
  • Stale: past max-age. If a copy has age: 4000 and max-age=3600, it is already expired when it arrives.

When a Copy Expires, Ask Instead of Delete

cache origin GET + If-None-Match: "32b7140e..." 304 Not Modified (no body)same ETag cache-control: max-age=3600→ new TTL (look in order: ① s-maxage ② max-age ③ Expires) age: 0→ seconds already used 200 + new filedifferent ETag
A refresh asks the origin with the ETag, and a 304 restarts the fresh time without sending the file again.
  • This action is called a refresh. With a 304, the copy stays the same and the fresh time starts again. A 304 has no body, but it still needs a round trip.
  • CDN settings can override the origin values, for example a fixed TTL or a cap (TTL Is Set in Three Places).
  • The Age on a 304 counts too: with max-age=3600 and age: 4000, the copy is still expired after the refresh.

The CDN also tells the browser how much time is left. I saw a script come back with max-age=2251, because the CDN copy had only 2,251 seconds left. Later I learned that a copy with a large Age is almost impossible for the next layer to keep (Stale-While-Revalidate: Serve Old First, Refresh in the Background).

A cache in front is only as fresh as the copy it receives.

Sources: RFC 9111: HTTP Caching

Related: What a CDN Is, Cache Headers: Who Sets, Who Reads, TTL Is Set in Three Places, Stale-While-Revalidate: Serve Old First, Refresh in the Background. Or go back to the series overview.

↑↓ navigate ↵ open esc close