Using Cache-Control and CDNs to Improve Performance and Reduce Latency
12 minWe 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
- The Cache-Control Directives
- Cache Invalidation
- Configuring Cache-Control for your Sites and Resources
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:
- 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 over 250 locations across the globe as part of its edge network.
- 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.
- 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:
- 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.
- 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.
- Reduce bandwidth costs: Important if your website generates a lot of traffic.
Broad Overview of How Caching Works
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.
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.
Below is a non-exhaustive list of the different request and response directives you can use for Cache-Control
.
Request Directives for Cache-Control
Expiration
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
.
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.
Extension
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
Caching
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.
Expiration
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.
Extension
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
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.
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.
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 Case | Best Cache Practice | Example 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 join the community.