In HTTP caching, what does it mean for a stored response to be fresh versus stale, and what is a cache allowed to do once a stored response goes stale?
answer
- fresh = age < lifetime → no network at all
- stale ≠ deleted, ≠ wrong
- stale → revalidate; 304 restarts the clock
- freshness saves the round trip, validation saves the bytes
- age counts time in upstream caches too
basics
~20 sA stored response is fresh while its age is below its freshness lifetime; it can be reused with no network trip. Once age exceeds that lifetime it is stale — which does not mean deleted. The cache normally revalidates with the origin, and a successful check makes the copy usable again with a reset clock.
solid answer
~60 sHTTP caching has two modes of reuse. **Fresh** means the stored response's current age is less than its freshness lifetime, so the cache serves it directly with no contact with the origin at all. This is the fast path and the whole point of caching. **Stale** means the age has passed the lifetime. Crucially it does not mean wrong, and it does not mean evicted — the bytes are still there. A stale response simply may not be reused *without checking*. The normal next step is **revalidation**: the cache asks the origin whether its copy is still good. If the answer is yes, the origin sends a small `304 Not Modified` with no body and the cache serves its stored bytes and restarts the freshness clock. If the resource changed, the origin sends a full `200` and the cache replaces its copy. So the lifecycle is: store → fresh (free reuse) → stale (reuse only after a cheap check) → refreshed or replaced. Freshness saves the round trip; revalidation saves the payload. Only `no-store` keeps a response out of this cycle entirely.
code
http · 6 linesHTTP/1.1 200 OK
Date: Mon, 12 Aug 2026 10:00:00 GMT
Age: 120
Cache-Control: max-age=300
ETag: "v7"
Content-Type: text/htmlgo deeper
Get the two states and the transition right: fresh means serve it now, stale means check first, and checking is usually cheap.
Explain the two distinct savings — round trip versus payload — and that a 304 refreshes the stored response rather than replacing it.
Add what a cache may do beyond revalidating: serve stale under explicit permission, and error rather than serve stale under must-revalidate.
Frame lifetime selection per content class as a deliberate tradeoff between origin load, correctness window and the cost of being unable to recall a long-lived copy.
## The two savings a cache can make An HTTP cache can save two different costs, and freshness versus staleness is exactly the line between them. 1. **The round trip.** While a response is fresh, the cache answers with zero network contact. Latency is essentially nil and the origin sees nothing. 2. **The payload.** Once a response is stale, the cache can still avoid transferring the body by asking the origin a yes/no question. If nothing changed, the answer is a headers-only `304 Not Modified` — a few hundred bytes instead of possibly megabytes. Understanding this split is what makes caching headers make sense: freshness settings decide how often you pay a round trip, validators decide how much that round trip costs. ## Freshness lifetime and age A stored response is **fresh** while `current_age < freshness_lifetime`. *Freshness lifetime* is how long the origin says the response may be reused without checking. It comes from `Cache-Control: s-maxage` in a shared cache, else `Cache-Control: max-age`, else the `Expires` header minus `Date`, else — if none of those are present — a heuristic the cache derives itself. *Current age* is not "time since I stored it". It is time since the response was **generated or last validated at the origin**, which includes any time it already spent sitting in caches upstream of you. The `Age` response header is how upstream caches report that, and it is why a CDN's response can arrive already half-expired. ## What stale is not Three misconceptions worth dismantling: - **Stale does not mean deleted.** The stored response remains available and is the raw material for revalidation. Discarding it on expiry would throw away the ability to receive a 304. - **Stale does not mean wrong.** Most resources do not change on any particular schedule, so a stale copy is usually still byte-identical to the origin's — which is precisely why revalidation so often returns 304. - **Stale does not mean a full re-download.** That only happens if the response has no validator, or if the resource genuinely changed. ## What the cache may do with a stale response Once stale, a cache has a few options, ordered by how commonly they apply: 1. **Revalidate.** Ask the origin whether the stored copy is still current. A `304` refreshes the stored response — it is served to the client and its freshness clock restarts from the new response's headers. A `200` replaces it. This is the default path. 2. **Serve it stale, where permitted.** The origin can explicitly allow this with `stale-while-revalidate` (serve now, refresh in the background) or `stale-if-error` (serve when the origin is failing), and a client can request it with `max-stale`. A response marked `must-revalidate` or `no-cache` forbids it outright, and then a cache that cannot reach the origin must return an error such as `504` rather than stale content. 3. **Evict it.** Caches are finite; a stale entry is a natural eviction candidate, but eviction is a storage-management decision, not something the protocol requires at expiry. ## Why the design is shaped this way The alternative designs are both worse. If caches had to check every time, you would pay a round trip on every asset on every page — the latency that freshness eliminates. If caches never checked, you could never correct a mistake short of changing the URL. The fresh/stale split lets an origin choose its position on that continuum per resource: a long lifetime for content-addressed assets that cannot change, a lifetime of zero with `no-cache` for a document that must always be current but is usually unchanged, and something in between for a catalogue page where a few minutes of drift is acceptable. ## Reading it off the wire Given a response with `Date: Mon, 12 Aug 2026 10:00:00 GMT`, `Cache-Control: max-age=300` and `Age: 120`, the response is 120 seconds old on arrival and has 180 seconds of freshness left in your cache. After that it is stale, and the next request triggers a conditional check rather than a fresh download — assuming the origin gave the response a validator to check against.
- If a stale stored response has no ETag and no Last-Modified, what happens on the next request?The cache has nothing to ask the origin about, so it cannot make a conditional request and cannot receive a 304. It must fetch the full response again, transferring the whole body even if nothing changed. That is why origins should always emit a validator on cacheable responses — it is what makes expiry cheap instead of expensive.
- Does a cache have to delete a response when it becomes stale?No. Expiry only removes the permission to reuse the response without checking; the stored bytes stay and are needed to benefit from a 304. Caches do evict entries, but that is driven by storage pressure and replacement policy, not by the freshness lifetime running out.
- What does a `304 Not Modified` do to the stored response's freshness?It refreshes it. The cache serves the stored body and updates the stored headers from the 304, so a new `max-age` or `Expires` restarts the freshness lifetime from that moment. The resource becomes fresh again without any body being transferred.
saying these in an interview costs you the question
- Saying a stale response is deleted or evicted the moment it expires
- Believing stale means the content is definitely out of date
- Thinking every expiry costs a full re-download regardless of validators
- Confusing age with time since the cache stored it, ignoring upstream cache time
- Assuming a cache always contacts the origin, even while the response is fresh