HTTP - Caching
How browsers cache HTTP responses using timeouts, ETags, and 304 Not Modified
Caching
Caching
- Loading a page is still a lot of requests, even with keep-alive
- CSS, JS, images, and fonts rarely change
- Sending the same file over the network every time is wasted work
- Caching: reuse a previous response instead of downloading it again
Who Stores the Cache?
- The browser stores the response
- Your server does not keep a cache of responses
- Your job is to send headers that tell the browser:
- How long this response can be reused
- How to check whether it is still valid
- Same URL later: the browser decides whether to reuse, revalidate, or request the resource
Fresh vs stale
- Fresh: the cached copy is still good. Use it. Do not talk to the server
- Stale: the cached copy might be outdated. Ask the server before using it
- Two independent mechanisms:
- Timeouts (
max-age,Expires): how long the copy stays fresh - Validators (
ETag,Last-Modified): how the server confirms it has not changed
- Timeouts (
Timeouts
Cache-Control: max-age
- Number of seconds the browser may reuse this response
HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 42
Cache-Control: max-age=3600
max-age=3600means this is fresh for one hour- After that, the copy is stale and a new request will be sent
Expires
- An exact time when the response becomes stale
Expires: Wed, 23 Sep 2026 19:50:00 GMT
- HTTP dates are GMT, not local time
max-ageis preferred- Less prone to time zone, daylight savings, etc. issues
- Easier to use
- If both are present, the browser uses
max-age
Other Cache-Control values
no-store— do not save this at all (passwords, private pages)no-cache— you may store it, but revalidate before using itprivate— only this user's browser may cache it, not a shared proxypublic— OK to store in a shared cache
Validators
When the Cache is Stale
- Timeout expired. Browser still has the old bytes
- Send a request to the server asking if the response is still current
- If it is, send a 304 Not Modified response with no body
- Prevents resending the same data and can reset the timeout
- If it is not current, send a 200 OK with the new response
- If it is, send a 304 Not Modified response with no body
ETag
- A fingerprint of this version of the resource
- Send it on 200 responses
HTTP/1.1 200 OK
Content-Type: text/css
Content-Length: 42
Cache-Control: max-age=10
ETag: "9e107d9d372bb6826bd81d3542a419d6"
- When the file contents change, the ETag must change
- A hash of the file bytes is a great way to do this
If-None-Match
- Once the copy is stale, the browser sends the ETag back
GET /public/style.css HTTP/1.1
Host: localhost:8080
If-None-Match: "9e107d9d372bb6826bd81d3542a419d6"
- Compare it to the ETag of the file currently on disk
- If they match, send a 304
- If they are different, send a 200 with the new etag
304 has no body
HTTP/1.1 304 Not Modified
ETag: "9e107d9d372bb6826bd81d3542a419d6"
Cache-Control: max-age=3600
- 304 means "your copy is still current"
- No body in the response
- Not Content-Length/Content-Type headers
- The browser already has the bytes. You are only confirming they are current
- You may send the ETag and
Cache-Controlagain so the copy becomes fresh
Vary
- By default the cache key is the URL
- Sometimes the same URL serves different content depending on a request header
Vary: Accept-Language
GET /pagewithAccept-Language: enandAccept-Language: esare different cache entries- If you skip
Vary, an English user can be served the Spanish page from cache - Common on
AcceptandAccept-Language