Caching

Caching means storing a given response so it can be served back immediately when requested. Caching improves performance (reduces network transmission) and eases the load of the origin server.

Types of caches

There are two types of cache:

  • Shared proxy caches cache for multiple users ("shared cache")
  • Private browser caches cache for only a single user ("private/local cache")

Reading from the response headers, they will determine what responses should be cached, and how (e.g. how long, how to revalidate for freshness).

Shared and private cache
Shared and private cache (Source: MDN)

Cache validation

Once a cached resource goes past its expiration time (determined by the max-age directive or Expire response header), when its value is about to be used again (e.g. when the user presses the reload button), it must either be:

  • validated (rechecked by the origin server that the cached copy is still valid), or
  • fetched again (e.g. if the resource has changed).

How it works

Validation can only occur if the server provided either a strong validator (ETag) or a weak validator (Last-Modified) in the response header.

When the client asks for a resource by including the If-None-Match (if the resource is using Etag) or the If-Modified-Since request header, the server may respond with:

  • 200 (OK) indicating the client should use this newly sent response
  • 304 (Not Modified) (with an empty body, optionally including headers that update the expiration time of the cached resource) indicating the client should use its cached copy

Response headers

Cache-Control

Controls caching behavior. Multiple directives should be separated by comma.

"Revalidation for freshness" here is done by the client sending (via If-None-Match or If-Modified-Since request header)

Cachability directives
  • no-store don't store response in any cache
  • no-cache the response can be stored in any cache, but it must always be revalidated for freshness by the origin server before its value can be used.
  • public the response can be stored in any cache
  • private the response can be stored only in a browser's cache
Expiration directives
  • max-age=<seconds> how long the resource should be considered fresh (relative to the time of request)
  • s-maxage=<seconds> overrides max-age, but only for shared caches (private caches will ignore it)
Revalidation directives
  • must-revalidate local copy can be used if it's younger than max-age; otherwise it must first be revalidated for freshness by the origin server. The origin server can respond
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: public
Cache-Control: private
Cache-Control: max-age=<seconds>
Cache-Control: s-maxage=<seconds>
Cache-Control: must-revalidate
Cache-Control: proxy-revalidate
Cache-Control: no-transform

ETag

Indicates the version of a resource. You can use the content hash, last modified timestamp, or a revision number.

If this response header is present, the client will send If-None-Match on future requests of the resource to validate the cached resource.

You can use "strong" or "weak" ETag value:

  • Strong Etag (e.g. ETag: "1f2b3d") If two strong ETags match, it means the resource is byte-for-byte identical. (It can be cached for byte range requests.)
  • Weak Etag (e.g. ETag: W/"1f2b3d") If two weak ETags match, it means the resource is semantically equivalent, but not necessarily byte-for-byte identical (e.g. different date of generation in the footer). (It cannot be cached for byte range requests.)
ETag: "<etag_value>"
ETag: W/"<etag_value>"

Last-Modified

Indicates the time at which the resource was last modified, e.g. Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT (it's precise up to seconds, and always in GMT).

If this response header is present, the client will send If-Modified-Since on future requests of the resource to validate the cached resource.

Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

Age

Indicates the time in seconds the resource has been in a proxy cache.

Age: <delta-seconds>

Expires

Indicates the time after which the response is considered stale.

(If there is a Cache-Control response header with the max-age or s-maxage directive, this header is ignored.)

Expires: Wed, 21 Oct 2015 07:28:00 GMT

Vary

Indicates the response should be considered unique if any request/response header listed on Vary differs in value.

Any cache should remember the combination of header values listed on Vary, and should only give the cached resource copy if all header values match. Otherwise, a fresh response must be requested from the origin server.

Using Vary: * means each requests should be treated unique and should not be cached. (Consider using Cache-Control: no-store instead.)

Since Accept-Encoding: gzip,deflate, Accept-Encoding: gzip are considered different, to avoid unnecessary requests and duplicated cache entries, caching servers should consider normalization (i.e. preprocessing the request by normalizing/rewriting the value of the request headers).

Vary: *
Vary: <header-name>, <header-name>, ...

Request headers

Cache-Control

Requests caching behavior. Multiple directives should be separated by comma.

Cachability directives
  • no-store don't store the request/response in any cache
  • no-cache don't use any cached response, unless it has been revalidated for freshness by the origin server
  • no-transform don't let any intermediary servers transform the response (e.g. to compress the images)
  • only-if-cached only send cached response, otherwise send a 504 (Gateway Timeout) response
Expiration directives
  • max-age=<seconds> asks for response whose age is not greater than the given number of seconds
  • max-stale=<seconds> it's acceptable to give stale response (exceeded its freshness lifetime), as long as it hasn't been stale for more than the given number of seconds
  • min-fresh=<seconds> asks for response which would still be fresh for the given number of seconds (the freshness lifetime is no less than its current age plus the specified time in seconds)
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: no-transform
Cache-Control: only-if-cached
Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>

If-None-Match

Requests the server to only send a full 200 (OK) response if it doesn't have an ETag matching the given ones. Otherwise, the server will send 304 (Not Modified) response with an empty body.

  • <etag-value> may be prefixed with W/ to indicate weak etag.
  • * a special value to represent any resource (e.g. used with PUT to check if the same resource has been uploaded before)
If-None-Match: "<etag-value>"
If-None-Match: "<etag-value>", "<etag-value>", ...
If-None-Match: *

If-Modified-Since

Requests the server to only send a full 200 (OK) response if the resource has been last modified after the given time. Otherwise, the server will send 304 (Not Modified) response with an empty body.

Used in conjuction with Last-Modified response header. Ignored if If-None-Match is also present.

If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

References