4 minutes read

Cache-Control Headers Explained

Cache-Control Headers (CCH) are used between the server and the client. CCH is the HTTP header type and it can be seen in action in the browsers. Headers, in general, regardless of the type can be seen in HTTP requests as well as in HTTP responses. So, we then have request and response headers. The same applies to CCH. Both header types are used to control the cache, on the server, or on the client-side.

Request headers contain directives that dictate how the cache on the server is behaving. Response headers are doing the same for the client.

Let's check out some common request Cache-Control Headers:

  • Cache-Control: max-age=<seconds>
  • Cache-Control: no-cache
  • Cache-Control: no-store
  • Cache-Control: no-transform
  • Cache-Control: max-stale[=<seconds>]
  • Cache-Control: min-fresh=<seconds>
  • Cache-Control: only-if-cached

Now, let see some of the common response Cache-Control Headers:

  • Cache-Control: no-cache
  • Cache-Control: no-transform
  • Cache-Control: no-store
  • Cache-Control: public
  • Cache-Control: private
  • Cache-Control: proxy-revalidate
  • Cache-Control: must-revalidate
  • Cache-Control: max-age=<seconds>
  • Cache-Control: s-maxage=<seconds>

Extension Cache-Control Headers:

  • Cache-Control: immutable
  • Cache-Control: stale-while-revalidate=<seconds>
  • Cache-Control: stale-if-error=<seconds>

How does Cache-Control Headers affect responses

Every header value CCH contains a directive that carries specific instructions on how to handle the response in the cache:

no-cache directive tells the server it goes through that it can be cached, however, it must be revalidated with the server it originates from. Otherwise, the resource is going to be cached with mandatory revalidation. It is wrongly interpreted as a directive that forbids caching. In reality, it does behave like it's forbidding caching because of the revalidation process.

no-transform forbids caching servers between client and server to change the body of the response. We could be curious why would any caching server change the body. The answer is - servers with specific functionality of optimizing the response for better performance. This functionality will be prevented by using no-transform.

no-store prevents caching of the response anywhere. This directive overwrites any other directive that might seem as bypassing option for no-store.

public directive tells every caching server between client and server it is ok to cache the response. The browser too. It's important to note that even non-cacheable response will be cached.

private directive instructs browser to cache the response. Only browser! Caching servers between client and server will not be affected by this directive.

proxy-revalidate directive instructs caching servers that after cached response expires server must revalidate successfully with the origin server. This directive does not affect browsers.

must-revalidate directive instructs browsers (client caches) to revalidate the cached response after it expires.

max-age=<seconds> tells caching servers and client cache for how long the response should be cached. This time is defined in seconds. Numbers of seconds in this directive is relative to time of the request.

s-maxage=<seconds> instructs caching servers (not affecting private caches - clients) to override the max-age and Expires.

max-stale[=<seconds>] is a request header directive that indicates that browser (client) can accept stale/expired resource from caching server. Number of seconds in it defines the maximum time for which the resource should be stale.

min-fresh=<seconds> request header directive tells the caching server that browser (client) wants the resource that will be valid for at least X number of seconds.

only-if-cached tells caching servers to respond with resources from cache only. If there is no cached resource to be sent, the server should return 504 Gateway Timeout.

  • If-Modified-Since, If-None-Match won't have an effect if only-if-cached is set.

immutable directive tells the caching servers and client that response will never change. This means that using conditional request headers like If-Modified-Since and If-None-Match will not have an effect. Essentially, if the content doesn't change then conditional request doesn't make sense.

stale-while-revalidate=<seconds> indicates that client will accept the stale (expired) resource and check for the fresh version in parallel. Seconds in the directive are indicating how long the client will accept the stale response.

stale-if-error=<seconds> is related to the previous one where it indicates that the client will accept the stale resource if the parallel check for fresh one fails. The time component tells for how long.

Related article: How HTTPS Protocol works