HTTP caching is one of the highest-leverage performance tools available and one of the easiest to misconfigure. The headers interact, some combinations cancel each other out, and the failure modes are asymmetric: too aggressive and users see stale content you cannot recall, too conservative and you cache nothing. Getting the edge and origin layers to agree is part of our cloud and infrastructure work.
01The directives that matter
Most caching behaviour comes from a small number of Cache-Control directives. The distinction between freshness and revalidation is the one that causes the most confusion - one controls how long a response may be used without asking, the other controls what happens afterwards.
| Directive | Meaning | Use for |
|---|---|---|
| max-age=N | Fresh for N seconds | Almost everything cacheable |
| s-maxage=N | Freshness for shared caches only | Different CDN and browser lifetimes |
| no-cache | Store, but revalidate before use | Content that changes unpredictably |
| no-store | Do not store at all | Genuinely sensitive responses |
| immutable | Will never change - skip revalidation | Content-hashed assets |
| stale-while-revalidate=N | Serve stale, refresh in background | Avoiding latency on refresh |
02no-cache does not mean do not cache
This naming is the single most common source of confusion. no-cache permits storage and requires revalidation before reuse - the response is cached, it is just checked first. no-store is the directive that means do not keep it.
Using no-store where no-cache was intended eliminates caching entirely and produces a full transfer on every request. Using no-cache where no-store was required means sensitive responses are being written to disk in intermediaries, which is a different and more serious problem.
03Immutable assets are the pattern to reach for
The hardest caching problem - invalidation - disappears when content never changes. Content-hashed filenames mean the URL changes whenever the bytes change, so the old URL can be cached effectively forever and the new one is simply a different resource.
This is why build tools hash asset filenames. Apply the same thinking beyond static assets wherever you can: version the cache key rather than mutating the value, and invalidation becomes a write to a new key instead of a coordinated purge that may not propagate.
04ETag and the revalidation round trip
An ETag lets a client ask whether its copy is still current. If it is, the server returns 304 with no body - saving bandwidth but not the round trip. That distinction matters: revalidation is cheaper than a full transfer and considerably more expensive than a cache hit with no request at all.
There is a subtle trap when a CDN sits in front. If the origin returns 304 on revalidation, that response carries no body and no fresh headers, so any header the CDN was caching stays as it was. We have seen a correctly configured response header silently missing in production for exactly this reason - the origin was right and the edge was serving an older header set.
- ETags save bandwidth, not round trips.
- Weak ETags are fine for most content and cheaper to generate.
- A 304 carries no headers - anything header-based can go stale at the edge.
- Set headers at the edge when they must survive revalidation.
05Vary is easy to get expensive
Vary tells caches that responses differ by a request header, so a separate entry is stored per distinct value. Vary on something with many possible values - a full User-Agent string being the classic mistake - and your hit rate approaches zero because almost every request is a unique cache key.
Keep the varied dimensions few and low-cardinality. Accept-Encoding is fine. Vary on Cookie is usually a mistake for anything you actually want cached, because cookie values are effectively unique per user.
06Stale-while-revalidate for the best of both
This directive lets a cache serve a stale response immediately while fetching a fresh one in the background. The user never waits for a revalidation, and the cache converges to current within a short window.
It suits content where being a few seconds behind is harmless - listings, feeds, dashboards, most marketing pages. It also removes the latency spike that otherwise occurs whenever a popular entry expires, which is the same thundering-herd shape that causes cache stampedes.
Topics
Priya Iyer
Staff Engineer · SyncTrix
Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.
Building something like this?
SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.