ETags and conditional requests: how browsers avoid re-downloading unchanged files
ETag header, If-None-Match, Last-Modified, If-Modified-Since, 304 Not Modified, weak vs strong ETags, conditional GET
ETags and Conditional Requests
ETags let browsers verify whether a cached resource is still fresh without downloading the full content again. If unchanged, the server responds with 304 Not Modified — no body, just headers. This saves bandwidth and reduces time-to-content on repeat visits.
How ETags work
# First request — full response
GET /style.css HTTP/1.1
# Server response
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: max-age=3600
[body: 40KB of CSS]
# After max-age expires, browser sends conditional request
GET /style.css HTTP/1.1
If-None-Match: "abc123"
# If file is unchanged, server returns no body
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600Last-Modified alternative
Instead of an ETag, servers can send a Last-Modified timestamp. Browsers send it back as If-Modified-Since. ETags are preferred because they handle sub-second changes and file regeneration (same content, new timestamp) better than dates alone.
Strong vs weak ETags
A strong ETag ("abc123") guarantees byte-for-byte identity between the cached and current copy. A weak ETag (W/"abc123") means semantically equivalent but may differ in bytes — for example, gzip vs brotli encoding of the same content. Use strong ETags when byte-perfect caching is required; weak ETags are acceptable for range requests and content negotiation scenarios.
# Test ETag behavior with curl
curl -I https://example.com/style.css
# Then use the returned ETag:
curl -H 'If-None-Match: "returned-etag"' -I https://example.com/style.css