Glossary
Conditional request
A conditional request asks the server to return a resource only if it has changed since the copy the client already holds. The client sends a validator — an entity tag or a last-modified date — and the server answers either with the full resource or with a short 304 Not Modified carrying no body. It saves the download and not the round trip, which is why a validator is a weaker form of caching than a freshness lifetime.
The two validators
An entity tag is an opaque string the server assigns to a specific version of a resource. The client returns it in If-None-Match.
A last-modified date is a timestamp. The client returns it in If-Modified-Since, and the comparison has one-second resolution — which is too coarse for resources that change more often than that.
Entity tags are the stronger of the two because they identify a version rather than a moment, and because they work for resources whose content changes without the timestamp moving.
A server may send both. A client that has both sends both, and the entity tag takes precedence.
Why a 304 is worse than no request at all
A 304 avoids transferring the body and still costs a full round trip: name resolution if needed, connection if needed, request, response.
On a fast connection that is a few milliseconds. On a mobile connection it is most of the delay, and it is paid once per resource per page view.
A freshness lifetime avoids the request entirely, which is why a long lifetime on fingerprinted assets is the configuration that actually saves time.
So a resource with an entity tag and no lifetime is not cached in the sense that matters — it is revalidated, continuously.
Where entity tags cause trouble
Generated per server instance rather than per content version, so a load-balanced site returns a different tag from each machine and every revalidation is a miss.
Derived from a filesystem inode, which changes on redeploy even when the file does not.
Combined with compression negotiation incorrectly, so the compressed and uncompressed forms share a tag and a cache serves the wrong one.
None of these breaks a site visibly. They quietly turn every revalidation into a full transfer, which looks like caching not working.
How it is observed
Entity tags and last-modified dates really returned for static resources are read, alongside the freshness directives.
A resource carrying a validator and no lifetime is reported as revalidating rather than as cached, which is the distinction that matters.
Whether the validators are stable across servers cannot be established from one observation, since a scan reaches whichever instance answered.
Conditional requests are not issued during a scan, so the 304 behaviour itself is not exercised.
Frequently asked questions
- Is an ETag enough to call a resource cached?
- Not in the sense that saves time. It avoids re-downloading the body and still costs a round trip on every page view. A freshness lifetime is what avoids the request.
- ETag or Last-Modified?
- Entity tags, where you can generate them from content. Last-modified has one-second resolution, which is too coarse for anything that changes more often.
- Why does my caching seem not to work behind a load balancer?
- A frequent cause is entity tags generated per server instance rather than per content version, so a client revalidating against a different machine always gets a full response.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website