Using Cache-Control and CDNs to Improve Performance and Reduce Latency

May 18, 2021

Yann Léger

Yann Léger

Édouard Bonlieu

Édouard Bonlieu

Alisdair Broshar

Alisdair Broshar

We believe latency and caching are crucial subjects we need to talk about. Loading times are a critical component in the user's experience for all apps and websites: new pages and screens should load in less than a second for end-users to have a smooth experience.

At Koyeb, we provide a global edge network with a built-in CDN to help you achieve that with caching and TLS termination at the edge, but there is an important topic which is always your responsibility when you use a CDN: Cache-Control HTTP headers.

In this post, we review how caching works and dive into Cache-Control HTTP headers, which are used to configure how your content is cached across the internet and end-user devices. We will explore how to configure them and what guidelines to follow when using them.

Getting caching right can be tricky and getting it wrong is headache-inducing. Learning about it is even more critical now that over 50% of the internet's traffic is served towards mobile devices where interactions are highly latency-sensitive and long loading times will trigger high bounce rates.

Table of Contents

Caching Types, Benefits, and Overview

Caching technologies temporarily store data between origin servers and end-users to improve site performance and reduce latency for the requests of your web services and apps.

At a high level, Cache-Control is an HTTP header that specifies caching policies for client requests and server responses. These policies, which are configured parameters, determine how resources are cached, where, and their time-to-live before expiring.

  • Cacheable refers to whether the content can be stored in cached;
  • Where is important given cache can exist locally in a browser or in a server;
  • and time-to-live is the amount of time content is considered fresh.

Types of Web Caches

It is important to understand there are 3 main locations where content is cached:

  1. Gateway Caches - They are a component of your infrastructure designed to increase your application performance. Koyeb provides Gateway Caches, also called Reverse Proxy Cache, in 55 locations across the globe as part of its edge network.
  2. Proxy Caches - Deployed by businesses or internet service providers (ISPs) to create a type of shared cache. These caches reduce latency and network traffic to your origin server but you have no direct control over them. HTTP Headers are critical to indicating what they can cache.
  3. Browser Caches - Use a part of the hard drive or memory of the end-user device to store a copy according to the application's cache settings.

Cache Benefits

There are 3 key benefits to caching:

  1. Reduce response time: Caching improves response time by storing rendered pages and assets in a location nearer to your end-users. When content is served from a cache, it removes the compute time needed to render the page and reduces the network round trip time needed to hit the server.
  2. Reduce load on your application: By serving cached assets directly from the CDN or the browser, you reduce the number of requests that hit your origin servers and infrastructure.
  3. Reduce bandwidth costs: Important if your website generates a lot of traffic.

Broad Overview of How Caching Works

cache schema

The Cache-Control Directives

HTTP 1.1 improved caching possibilities with Cache-Control

Before diving into how to use Cache-Control, it is helpful to review briefly the history of configuring cache.

HTTP 1.0 Caching Limitations

With HTTP 1.0, caching headers were limited to:

  • Expires: This is an age limitation for cached content, and it is configured to a specific date and time. It is an absolute limit that is not relative to the client's request.
Expires: Mon, 22 Apr 2021 23:59:25 GMT
  • Pragma: no-cache This is a no-cache meta tag that does not necessarily keep a web page fresh.

HTTP 1.1 New Caching Headers

The release of HTTP 1.1 addressed the limitations of caching with Expires: and Pragma: no-cache by adding a new class of headers, Cache-Control. These new headers offer more control over their content than previously possible with HTTP 1.0.

Depending on your needs, you can use multiple Cache-Control directives by separating each of them with a comma.

Cache-Control: public, max-age=60, stale-while-revalidate=180

Below is a non-exhaustive list of the different request and response directives you can use for Cache-Control.

Request Directives for Cache-Control


max-age - The maximum number of seconds a representation is considered fresh. This time limit is relative to the client's request whereas the time limit of Expires is absolute. If both headers are used, max-age takes priority over Expires.

Cache-Control: max-age=3600

Note: max-age is an improvement from expires given that it is a relative time-to-live for each request. The relativity of the max-age directive mitigates for unintentional DDoS or spikes in traffic.

max-stale - The maximum number of seconds a client will accept a stale response. A stale response is a response that is older than the time specified by max-age.

min-fresh - The minimum number of seconds a client wants a response to be fresh. A cache is fresh as long as the amount of time of max-age has not expired.

no-cache - The stored response must be validated with the origin server before being used. This is a good option if important information on your site changes, but you still want the benefits of caching.

no-store - Blocks a cached copy from being stored anywhere, be it in a local cache, a browser, or third-party proxy. This directive forces each request to travel to the origin server to access the content. It is expensive and slower, but it does guarantee up-to-date content. Important note: this directive cannot be used with other directives.


stale-if-error- If there is an error during a check for a fresh response, this is the number of seconds the client will accept a stale response. If the number of seconds has not been surpassed, stale-if-error returns a cached response.

Response Directives for Cache-Control


private - The response can only be stored on a browser's response. Use this directive when the content is meant for a specific end-user. For example, an individual email account.

public - The response can be stored on any cache. Use this directive when anyone can see the content. For example, a product's pricing page.

no-cache - The stored response must be validated with the origin server before being used. This is a good option if important information on your site is updated, but you still want the benefits of caching.

no-store - Blocks a cached copy from being stored anywhere, be it in a local cache, a browser, or third-party proxy. This directive cannot be used with other directives.


max-age - The number of seconds after the initial request that a representation is considered fresh. This is relative compared to Expires, which is absolute. max-age takes priority over Expires.

Note: max-age is an improvement from expires given that it is a relative time for each request rather than an absolute expiration date shared across clients. The max-age directive mitigates for unintentional DDoS or spikes in traffic and is indifferent towards the clocks or timezones for each client.

s-maxage - Like max-age, but only for shared proxies, like CDN providers.

must-revalidate - Forces request to validate with the origin server.

proxy-revalidate - Like must-validate but only for proxy caches, not private caches.


stale-while-revalidate - Used with max-age, it is the number of seconds a client will accept a stale response.

When a client request comes in for content, max-age is used to determine if the response can be cached content. If the max-age time has expired, stale-while-revalidate is used to determine if the request will accept stale content.

This directive strikes a balance between loading cached content quickly and ensuring fresh content. It is most often used with asynchronous background workers to check for fresh content.

Important note: you can only use this cache-control header with Chrome and Firefox. Other browsers will ignore this header and only use max-age.

stale-if-error- Returns a cached response if an error is encountered, regardless of the content's freshness.


Validators check changes between the cached copies and the files at the origin server. If the content is valid, the client's request does not need to be sent all the way to the origin server.

Last-Modified - Sent by the origin server in its response, Last-Modified marks the last modification to the resource. This validator is often used alongside the Expires header.

Last-Modified: Fri, 8 Jan 2021 11:32:14 GMT

If-Modified-Since - This is a conditional validator that is sent by the client to check when the file was last changed to determine if the cached content matches what exists at the origin server. If the cached content matches what exists at the origin server, the origin server returns a 304 to say it is ok to return the cached copy.

In addition to Cache-Control headers, HTTP 1.1 also introduced a new validator:

ETag - The entity tag hashes a file byte by byte to create a digital fingerprint of the file. These unique identifiers determine if the cached content in a browser matches the content at the origin server.

Once a response, be it an ETag or conditional validator, is validated with the origin server, the origin server returns a 304 Not Modified response to the client. See illustration for more.

Cache Invalidation

Caching is great for reducing latency and bandwidth costs, but what happens when you change or update content on one of your web pages? If that content is cached, you will need to find ways to invalidate those cached copies to serve your users your fresh and up-to-date content.

Cache invalidation is a way to deliver new content to your users by evicting an object from a CDN's cache. There are different methods to perform cache invalidation, the details of which vary across CDN providers.

This is easier said than done because there is an inherent trade-off for latency between performing If-Modified-Since checks and serving potentially outdated cached content immediately.

Cache invalidation is usually reserved for special circumstances, as the best practice remains to configure a reasonable expiration time for your cacheable content with s-maxage. Also it is helpful to keep in mind that higher max-age times for private cached content make it more difficult to update content cached in local browsers.

Configuring Cache-Control for your sites and resources

There are lots of options for configuring a Cache-Control header. While there is not necessarily a standard, there are best practices that can guide you when configuring Cache-Control for your websites.

Tree Diagram Cache-Control

Cache Use Cases: A matter of how often you update your resources

You'll want to configure your Cache-Control duration parameters depending on how frequently data is updated on the origin server. For resources that do not change so often, you could set the max-age directive to 86400 seconds (equal to 24 hours) or even 31536000 seconds (equal to 1 year). In any case, another practice you'll want to keep in mind is keeping max-age for private and browser caches short while setting larger time-to-live values for s-maxage and public. Again, higher private time limits make it difficult to update content cached in local browsers.

Below is a table we've prepared outlining best practices for real-world examples.

Cache-Control Use Case Examples

Use CaseBest Cache PracticeExample Cache-Control
Static assets like images or fonts.Public cache control to cache the information on proxy servers.Cache-Control: public, max-age=31536000, immutable
Resources such as documents, images that are only available for one particular user or for authorized users.Cache on browsers, not on proxy servers.Cache-Control: private, max-age=3600
Content that changes, but not rapidly, and where multiple users will request the same content i.e., apps for news headlines or weather conditions.Public cache to proxy servers with the possibility to fetch stale content.Cache-Control: max-age=360, stale-while-revalidate=1200
Prevent returning errors if the origin server is unreachable when cache needs to be revalidated.Use stale-if-error to continue serving the page resources beyond the time expiration of the resource.Cache-Control: public, max-age=3600, stale-if-error=900
Dynamic content on social media feeds.Never cache this data.Cache-Control: no-store
Content that should be revalidated before being rendered.Guarantee the freshest content without sending a new response every time.Cache-Control: public, no-cache
Cached resources on your application deployed on Koyeb that require to be invalidated when a new version is deployed.Cache at the edge as Koyeb invalidates the cache each time a new deployment occurs.Cache-Control: s-maxage: 2592000

Caching with CDNs increases performance

Since CDNs store cached copies of your site and app in their network of data centers and proxy servers, CDNs shorten the distance between your users around the world and the server where your site or app is hosted on.

In addition to improving the latency experienced by your end-users, CDNs increase the performance of your sites and apps because gateway caching reduces the traffic that reaches the origin server.

Global load-balancing and built-in CDN with the Koyeb Serverless Platform

All serverless deployments on Koyeb benefit from the platform's native global load-balancing and CDN. Traffic is routed through the nearest edge location for the end-user to reduce delivery latency.

Koyeb is a developer-friendly serverless platform that hosts web apps and services, Docker containers, APIs, event-driven functions, cron jobs, and more!

Thanks to native support of popular languages and built-in Docker container deployment, you can use Koyeb's serverless platform to deploy your projects.

With Koyeb, you can scale like internet giants without their budget. See the benefits of going serverless, get started by signing up today and joining us on Slack.