One of the often overlooked superpowers of HTTP is the built in support for caching. This allows you to only spend the effort of generating a response every so often instead of having to generate it for each user individually. HTTP caching is one of the main strategies used in achieving scale and is also very important when improving performance.
Types of Caches
A HTTP cache is either a public or a private cache. A private cache is defined to only be used by one user. An example of a private cache is the browser cache. Private caches can cache both public and private responses.
Public caches are usually shared between multiple users. This means that any data you cache here should not contain sensitive or user-specific data. Examples of public caches are company wide caches, or caching proxy like Varnish you can deploy in front of your webserver. Public caches will only return cache items that are marked as public.
Time Based Caching
An easy way to implement caching is by specifying that a response is valid for a certain amount of time. For example if your resource is static and the same for all users you can set the following Cache-Control header:
Cache-Control: public,maxage=300 This will keep the resource in your cache for five minutes.
ETag Based Caching
If it is easy to generate a unique identifier that always changes when the response changes you can use that as an ETag identifier. This is done using the ETag Header:
ETag: W/"my-unique-identifier" Cache-Control: public,maxage=5,must-revalidate
This example will ask the cache to keep serving this response to requests coming in in the next 5 seconds. If a matching request comes in after the five seconds the cache will check whether it still has the last version using the If-None-Match header.
When you press F5 in a browser it will resend the request with a
Cache-Control: no-cache header. This asks all caches in between to return a fresh response. Beware that this might not work if you use a cache that does not adhere to the standard like Cloudflare.
The Vary header indicates how requests are matched to requests. If the URL and the varied headers match the response can be returned. If you have a HATEOAS Api you generally want to vary on the following headers:
- Accept-Language (if you have a multilingual endpoint)
Never add the User-Agent header though! This will likely make your website slower than not having a cache because of the huge amount of cache entries this can result in.
- Separate user data from static data.
- Put data that changes together into the same resource.
- Don't put data that changes independently into the same request. Instead use a resource that returns URLs to the child requests and leverage HTTP2 and caching.
- Instead of disabling caching, set the
maxageto a low value like 5 seconds.