An HTTP cache answers one question: may I serve this without asking? The answer is current_age < freshness_lifetime, and almost everyone computes the left side wrong.
RFC 9111 says:
apparent_age = max(0, response_time - date_value)
corrected_age = max(apparent_age, age_value) // trust the bigger one
resident_time = now - response_time
current_age = corrected_age + resident_time
Not now - Date. Two consequences fall straight out.
A response that spent 50 seconds in an upstream CDN arrives with Age: 50. Under max-age=60 it is fresh for ten more seconds, not sixty. And a slow request inflates apparent_age, so a response can be born already half-expired.
Paste your own headers and watch it: https://dev48.infy.uk/solve/day63-http-cache-simulator.html
freshness_lifetime, in strict order
-
s-maxage— shared caches only, and it beats max-age max-age-
Expires−Date - a heuristic — typically 10% of (Date − Last-Modified)
That last one surprises people. A response with no caching headers at all is still cacheable, and a CDN may hold it for hours. "I didn't set a cache header" is not the same as "it will not be cached".
no-cache is not no-store
-
no-cache— store it, but revalidate before every use. The response is written to disk. -
no-store— never write it down.
Ship no-cache when you meant no-store and private content sits in a shared cache. It is the most common mix-up in this area and it is a security bug, not a performance one.
Stale is not a failure
If you stored an ETag, send If-None-Match and take the 304 — one round-trip, zero bytes of body. A cache showing a 0% "hit rate" that is doing nothing but 304s is performing fine. Separate the counters or you will optimise the wrong number.
Vary, or one user's page served to another
The cache key is the URL plus the request headers named in Vary. Skip it and you do not get a slow site, you get a wrong one.
I ran 300 requests across three Accept-Encoding values through both keying strategies:
| keyed on | wrong bodies served |
|---|---|
| URL only | 201 of 300 |
| URL + Vary | 0 |
Two thirds of clients handed a body they cannot decode. With Vary: Cookie on a personalised page, that is one user's response going to another.
Every rule above is implemented by hand and checked — 30 assertions, including that the on-page directive table matches what the code actually does.
Top comments (0)