Historical archive
HTTP Cache Header Priority: Cache-Control, ETag, and Last-Modified
How browser freshness caching and conditional requests interact, including the precedence of Cache-Control, Expires, ETag, and Last-Modified.
Browser caching can be divided into two broad mechanisms: freshness caching and validation, also called conditional caching.
Freshness caching
After the first request, the browser can reuse a local response while it remains fresh without contacting the server. The main headers are:
Expires: <date>Cache-Control: max-age=<seconds>Cache-Control: no-cacheCache-Control: no-store
When both Expires and Cache-Control are present, Cache-Control takes precedence. Older advice often included both because Cache-Control was introduced in HTTP/1.1 while Expires existed in HTTP/1.0.
Conditional validation
Once validation is required, the browser contacts the server. If the representation has not changed, the server returns 304 Not Modified, allowing the cached body to be reused. Otherwise, it returns the new content.
- The server sends
ETag: "<value>", and the client later sendsIf-None-Match: "<value>". A match can produce a 304 response. - The server sends
Last-Modified: <date>, and the client later sendsIf-Modified-Since: <date>. If the resource has not changed since that time, the server can return 304.
Precedence
- A still-fresh response is reused before conditional validation is considered.
Cache-Controltakes precedence overExpires.ETaggenerally takes precedence overLast-Modifiedwhen both validators are available.- The older HTTP/1.0
Pragmaheader can affect caching, but it is rarely appropriate for modern response configuration.
Practical use
Bundlers commonly add content hashes to CSS and JavaScript filenames. Those immutable assets are good candidates for long-lived freshness caching because a content change creates a new URL. Images without hashes and HTML documents often need revalidation instead.
Browser behavior can differ when only validators are configured. One approach is Cache-Control: public, max-age=0, which allows shared caches while requiring immediate validation. Another is a very short positive max-age, which allows a brief fresh period before validation.
These notes reflect browser behavior observed when the original article was written. For production systems, review current HTTP caching specifications and CDN behavior.