Today’s blogpost will be about how you can improve your web application’s performance by adding caching using HTTP headers.
These headers allow us to limit the number of requests clients have to make, reducing load and improving client speed.
The main header we’ll be discussing is the cache-control header. This header is well supported. It allows us to instruct how proxies and the end user’s browser should handle a resource, cache-wise.
Since there are a few bugs around with “immutable”, “stale-while-revalidate” and “stale-if-error”, I’ll focus on the more basic options.
- The first thing to discuss is cacheability
- Public: response can be cached by any cache
- Private: response should not be cached by a shared cache, only by end user.
- No-Cache: can be cached, but each request has to be revalidated. This means that a shared cache can still hold on to a resource, but checks with the server before returning the cached content.
- No-Store: disables caching.
- The second part is about expiration.
Important options here are:
- max-age=[numberofseconds]: this option defines how long both local and shared cached can cache a response.
- s-max-age=[numberofseconds]: this option does the same as max-age, but excludes private (local) caches, so it only works for shared caches.
Combined these could form the following headers:
Example 1: local / private caching for one hour, not on shared proxy.
Cache-Control: max-age=3600 private
Example 2: can be cached both locally and on shared proxies for 24 hours.
Cache-Control: max-age=86400 public
Allows deciding if a response needs to be cached based on if a set of request headers differ.
You could use it to make sure content-negotiated content is cached correctly. For example, an XML response is already cached, you want to make sure to get another response when you ask for JSON by letting the caching vary on the “Accept-Encoding” headers.
There are two main ways of validating if a resource has changed.
If the browser sends back an ETag header with a specific value, the client can later pass on the ETag in a following request to check if the content has changed.
// SOME CONTENT
304 Not Modified
This is a similar principle, though it depends on a date instead of a unique value.
Last-Modified Sun, 05 Feb 2017 10:34:56 UTC
If-Not-Modified-Since: Sun, 05 Feb 2017 10:34:56 UTC
304 Not Modified
- immutable: this tells the agent requesting the content that this resource will never change. The browser would be allowed to keep the resource, even after a hard refresh.
- must-revalidate: this forces the client to check if the content has changed each time using the If-Modified-Since or If-None-Match mechanisms.
- proxy-revalidate: this forces the proxy to check if the content has changed each time using the If-Modified-Since or If-None-Match mechanisms.Cloudflare, these can take a great load of your servers.