HTTP Caching: In-Practice

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:

  1. personalized responses

  2. sensitive responses

  3. get the latest response (every request)

  4. static responses

  5. 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.

Managing different responses

1. Personalized responses

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
HTTP caching with Cache-Control: private

2. Sensitive responses

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
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.

3. Get the latest response (every request)

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
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.

4. Static responses

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 immutable responses
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).

5. Cacheable responses

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)
  • And many more

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:

  • how “fresh” do you expect your response to be over time ?
  • how frequently does it change ?

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.

1. Serve Stale while revalidating in the background

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
Managing stale response: stale while revalidate option

2. Revalidate then serve the response

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
Managing stale response: must revalidate option

3. Serve stale response if server returns an error response

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
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

  1. personalized response - use Cache-Control: private

  2. sensitive response - use Cache-Control: no-store

  3. Get the latest response (every request) - use Cache-Control: no-cache

  4. static response - use Cache-Control: immutable

  5. Cacheable Response - use Cache-Control: public, max-age=<time-in-ms> and decide on how to handle stale response

  • Common Strategies

    1. stale-while-revalidate - serve stale response, and revalidate with the server in the background

    2. must-revalidate - revalidate with the server, and either serve stale response or the new response (if it is available)

    3. 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!)

Enjoy the content ?

Then consider signing up to get notified when new content arrives!

Jerry Chang 2023. All rights reserved.