API Caching with HTTP Headers

Wed Jan 18 2023

6 min read

HTTP offers multiple headers for cache management of APIs. Learn how to use them properly in this guide.

What is API caching?

If more than one API requests result in the same response, we can store that response in a cache that is more readily available. When a client requests some resources, the request first goes through a cache and then to the server. If the cache contains the updated data, the request uses that data to satisfy the user's request.

This is API caching, and if done right, it results in better performance and latency while preventing excessive load on the API. Today, APIs use caching extensively. Therefore it is essential for developers to understand how caching works with HTTP.

API caching with HTTP headers

HTTP cache headers provide a way to manage API caching in web applications effectively. They let the API’s origin server communicate caching strategies to the client. By using HTTP headers, an origin server can specify the following:

  • Can the resource be cached or not?
  • How long can a resource be cached?
  • Who can cache the response?
  • When to validate the cache to get updated data.

With appropriate use of these headers, response times can be improved and server load reduced. So without further ado, let’s go through these headers.

Cache-Control

The Cache-Control header is used to specify browser caching policies in both client requests and server responses, allowing for the browser to store local copies of resources for faster retrieval. It specifies how and for how long the browser should cache the response.

For this purpose, the Cache-Control header has multiple directives. Here are the most commonly used directives:

  • max-age
  • private
  • public
  • no-store
  • no-cache
  • must-revalidate

Let’s go over them in detail.

max-age

This directive controls how long a resource can be cached. It includes a relative time value in seconds which means “X seconds after the response was generated by the origin server”.

It is a better alternative to the old Expires HTTP header. if both headers are present, Cache-Control takes precedence.

For example, ‘Cache-Control: max-age=1800’ means a resource can be retrieved from the cache for 1800 seconds or half an hour. You can also define the duration of shared caches by using the s-max-age directive.

public and private

The public and private directives of Cache-Control are used to specify who can cache the response. A resource can be cached by two actors, the end user or intermediary layers like CDNs which sit between the API server and the end user.

Cache-Control: public means both end user and CDNs or intermediary proxies can cache the response.

Cache-Control: private only allows the end user to cache the response.

must-revalidate

This directive prevents the use of stale resources. Stale resources are caches that are past the cache duration. must-revalidate specifies that stale resources must be revalidated against the origin server before being delivered to the client.

They are typically used in combination with max-age directives like this:

Cache-Control: max-age=604800, must-revalidate

You probably have questions. Why do we need must-revalidate if we have already defined the maximum duration? Because HTTP allows caches to use stale resources if they are disconnected from the origin server. must-revalidate specifically prevents this.

no-cache

Unlike its name, Cache-Control: no-cache doesn’t mean “no caching”. In fact, it specifies that the response can be cached. However, it asks the cache to be validated from the origin server before each reuse.

So if there is updated data on the server, the cache will be updated. Otherwise, it will be reused. no-cache is different from must-revalidate because it validates the cache every time, while must-revalidate only validates the cache once it is stale.

no-store

Cache-Control: no-store is the directive that puts a complete stop to caching. It prevents any kind of cache from storing the response whatsoever.

Check this comic for a quick recap of what we learned above.

Last-Modified

It is a response header that indicates the last time the resource was changed.

We can use it to determine if a resource has changed with respect to the cache. So we can detect if the cache is expired and should be fetched again.

For example, Last-Modified: Wed, 23 Jun 2022 01:12:22 GMT.

ETag

ETag has the same purpose as Last-Modified, but it uses a hashed value of the response instead of time. It helps to determine if the cache is up to date.

Whenever a resource is changed, the server generates a new ETag (hash value) for it and sends it to the client in response. The client caches this value. Now while making a request, the client sends the cached value to the server. If the server returns the same token, it means the resource hasn't changed, and the cache can be reused. Otherwise, the cache is updated.

For example, ETag: "1456jdgytn730dee". Etag is more accurate than the Last-Modified header.

Understanding cache headers of an API

Now, let’s put our learning to the test and see if we can understand the cache policies of an API through these headers. For this, I’ll use Rapid API Hub.

Why use Rapid API Hub?
Because it is bananas easy to try real APIs on it. So do a quick sign up and follow along with this guide.
Try it yourself

I’ll use a lyrics API which returns the lyrics of any requested song. Navigate to its page and subscribe to its basic free plan. Then, click the Test endpoint button to see the response. In the “Headers” section of the response, you can find the API’s cache headers.

As you can see, it has a cache-control: public, s-maxage=300 header, which means the cache can be stored by both private or shared caches and its duration is 300 seconds. It also has an ETag header for revalidation.

Wrap-up

We hope you find this guide on HTTP caching useful. If you have followed along, you can now understand and set caching policies of APIs with HTTP. Check our other guides on HTTP for more.