DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

current_age Is Not "now minus Date". That One Shortcut Explains Most Stale-Cache Bugs

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
Enter fullscreen mode Exit fullscreen mode

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

  1. s-maxage — shared caches only, and it beats max-age
  2. max-age
  3. ExpiresDate
  4. 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.

Repo: https://github.com/dev48v/solve-from-zero

Top comments (0)