Published on: Sat Oct 15 2022
In my previous article, HTTP caching: 6 Core Concepts, I covered the foundations of HTTP caching and how it works.
However, it may still be difficult to know when to use the various directives.
It’s great to learn these concepts but how do we put all that into practice ?
That’s what we’re going to go through in this guide.
When building software applications, you will inevitably have to make considerations in how you manage different types of responses (or data).
This is especially true when it comes to deciding which responses can be cached.
The responses tend to fall into these categories:
personalized responses
sensitive responses
get the latest response (every request)
static responses
cacheable responses
Note: This is not an exhaustive list but some of the common ones
The way you manage the caching for each of these responses are also going to be different.
In terms of HTTP caching, that means we need to provide the proper instructions to the cache on how to manage the responses.
The way you do this is by altering the Cache-Control directives.
Let’s go through them one-by-one.
When working with personalized responses, meaning this response is only intended for the client making the request, the obvious way to manage this is to not cache this response in our shared cache.
We don’t want to share this across the clients, but it is ok to store this on the client (browser cache).
The way we instruct our shared cache to not store this response is using this directive:
Cache-Control: private
What this translate to: This request should only be cached on the client cache (browser cache), and no where else
That also means all other shared caches should acknowledge this.
Example: User ID, analytics campaign id, user themes, client app configurations
HTTP caching with Cache-Control: private
When working with sensitive responses, or response that is intended only for the client, and it is sensitive (This should not be shared with other people).
It may be tempting to use the private directive, however, that still allows it to be cached on the client (browser cache).
When working with a sensitive response, its best we don’t store this response in any cache at all.
The directive to use:
Cache-Control: no-store
What this translate to: This response should not be stored in any cache (public or private).
Example: Banking information, personal information, addresses or any other sensitive information
HTTP caching with Cache-Control: no-store
💡 Tip: Using Cache-Control: no-store is great for preventing the caches from storing the resource.
However, when using this on a public resource, it can impact the browsing experience because the cache is used in the browser’s back and forward cache.
Use this directive sparingly. Prefer using no-cache and private before considering no-store.
If you would like the client to always have the latest responses from the server, you’d have to manage the validation request.
Meaning, you need to instruct it to always “check-in” with the server.
The directive to use:
Cache-Control: no-cache
Note: no-cache
does not mean “do not cache”, the name of it is a little mis-leading
What this translate to: The cache can still cache this response but it must always validate with the server before serving a response
By doing so, it allows the cache to know if it has the latest response.
HTTP caching with Cache-Control: no-cache
⚡️ Knowledge Boost: The validation request is the client’s way of “checking-in” with the server to know if the current response is up-to-date.If you like to learn more about this concept, check out my guide - HTTP Caching: 6 core concepts - validation request.
If you have responses that don’t change over time, then a good way to manage them is to instruct the cache to store them indefinitely.
The directive to use:
Cache-Control: immutable
What this translate to: The cache will not revalidate (or make a validation request) with the server because it will not change.
If you decide to use this directive, be sure that the cached response will not change over time because it may be difficult to invalidate it (or remove it) after the cache is set (especially if it is cached on the browser).
HTTP caching with Cache-Control: immutable
If invalidation is not an option, the alternative is to come up with a cache busting strategy.
For example, you may choose to append a hash id or version number to the file (which most modern frontend bundlers will support).
Cacheable are responses that can be stored in the cache.
The directives to use:
max-age
(TTL)public
(can be cached)s-max-age
(same max-age but specifically for shared cache)Example (Cache for 60 minutes):
Cache-Control: public, max-age=3600000
There are many combinations you can use when it comes to cacheable responses.
To me, a good way to determine what configuration to use is to understand:
Beyond just caching, you’d also need to decide how the cache should manage responses when they go stale.
This can be done through a Cache-Control directive.
Let’s go through the options that we have available.
In this option, the cache will first serve the stale response.
Then, in the background, the cache will validate the stale response with the server.
Managing stale response: stale while revalidate option
In this option, the cache will first send a validation request to the server before serving the client’s response.
So, that means it will verify with the server to check whether or not the response is still up-to-date.
This can lead to two outcomes:
No change - Serve the existing response
New response available - Serve the new response
Managing stale response: must revalidate option
This is not directly related to the stale responses but it does still applies.
If during the validation request, the response returns with an error, you can provide the stale-if-error directive to instruct the cache to use the stale response in this scenario.
The error codes that this applies to are: 500, 502, 503 or 504
Here is an example:
Cache-Control: public,max-age=3600000,stale-if-error=1800000
Managing stale response: stale if error option
So, there you you have it, the different ways to handle the common responses in a software application using HTTP Caching.
Let’s do a recap of the response, and how to manage each one using HTTP caching.
Common practical use cases
personalized response - use Cache-Control: private
sensitive response - use Cache-Control: no-store
Get the latest response (every request) - use Cache-Control: no-cache
static response - use Cache-Control: immutable
Cacheable Response - use Cache-Control: public, max-age=<time-in-ms> and decide on how to handle stale response
Common Strategies
stale-while-revalidate - serve stale response, and revalidate with the server in the background
must-revalidate - revalidate with the server, and either serve stale response or the new response (if it is available)
stale-if-error - allows for re-use of a stale response if error is received from the server (500, 502, 503 or 504)
That’s it! I hope this guide was helpful.
Also, be sure to check out my introduction guide on HTTP caching - HTTP caching: The 6 Core Concepts if you are looking to refresh your knowledge!
If you found this helpful or learned something new, please do share this article with a friend or co-worker 🙏❤️ (Thanks!)
Then consider signing up to get notified when new content arrives!