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.
max-age and 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 hasage: 4000andmax-age=3600, it is already expired when it arrives.
When a Copy Expires, Ask Instead of Delete
- 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
Ageon a 304 counts too: withmax-age=3600andage: 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.