Netlify’s global network supports a powerful range of cache features, most of which can be controlled via response headers. Now you don’t need to remember the different header names, directives and best practices to serve dynamic content that is fast and fresh.
#TL;DR
The cdn-cache-control
package has a simple API and sensible defaults that makes it easy to generate the headers needed for common caching tasks. It works anywhere that lets you set response headers, including Netlify Functions and Edge Functions, as well as natively within most web frameworks.
#Advanced caching on Netlify
Netlify supports several advanced cache directives that can be controlled via response headers. This is particularly useful for server-side rendering of web content, as it allows you to manually handle the invalidation of content, ensuring it stays fast and fresh. You can either set the cached responses to expire after a certain period, or purge it manually using the API. Using stale-while-revalidate
allows you to update content in the background without blocking requests.
#Manually setting headers
The full caching docs show how to have full control over your cache headers. You can use standard Cache-Control
and CDN-Cache-Control
headers, or target the cache more specifically with the Netlify-CDN-Cache-Control
header. You can also use the Netlify-Cache-Tag
header to invalidate cached content. On-demand invalidation allows you to purge content from the cache individually or in bulk. Because this is based on standard HTTP headers, it works with any web framework or function. You can see detailed guides for Remix and Astro in the Netlify developer hub.
#Using the cdn-cache-control
package
While manually setting headers gives you powerful, fine-grained control over the cache it can be hard to remember the correct headers to set, and the best practices for different types of content. I created the cdn-cache-control package to make it easy to generate the correct headers for common use cases. It extends the Fetch standard Headers
class, adding a simple, chainable API with sensible defaults for common use cases. It works by setting the Cache-Control
and Netlify-CDN-Cache-Control
headers to the appropriate values. It also has helpers for setting the Netlify-Cache-Tag
header, which is used to invalidate cached content.
#Installing the package
The package can be installed from npm:
#Using the package
The module exports a single class, CacheHeaders
, which is a subclass of the fetch Headers
class. By default it sets the Cache-Control
and Netlify-CDN-Cache-Control
headers to sensible values for content that should be cached by the CDN and revalidated by the browser. It also provides a chainable API for setting cache headers.
Like a regular Headers
object it can optionally be created with a value to pre-populate the header values. This can be an existing Headers
object, a plain object or array with existing header values. In that case it will default to using existing s-maxage
directives if present.
This example shows simple usage of the package:
This sets the Netlify-CDN-Cache-Control
header to public,s-maxage=31536000,durable,must-revalidate
, which tells the CDN to cache the content for a year and use Netlify’s Durable Cache for improved performance. It sets Cache-Control
to public,max-age=0,must-revalidate
, which tells the browser to always check with the CDN for a fresh version. You should combine this with an ETag
or Last-Modified
header to allow the CDN to serve a 304 Not Modified
response when the content hasn’t changed.
#Tagging content for invalidation
If you need to purge cached content you can add cache tags to your responses. This is particularly useful if your responses are based on an API that may change. You can use a webhook to purge the cache when content changes. For a complete example see the Astro guide, but read on for a simple guide.
In this example we add a cache tag to the response based on the id of the data:
#Expiring content
By default the CacheHeaders
class will cache your content for up to a year, or until you next deploy. If you want to set a different expiry time you can use the ttl
(time to live) method. You can pass a number of seconds to the ttl
method to set a different value. There are helper constants for common values:
#Faster loading with stale-while-revalidate
Enabling the stale-while-revalidate
directive tells the CDN to return stale content after it has expired, but then regenerate the content in the background. This is good for pages that may be slow to render, but where it’s ok to return stale content for a while. You can enable stale-while-revalidate
with the swr
method:
By default it will return stale content for up to a week, but you can pass a number of seconds to the swr
method to set a different value.
By default, calling swr
will expire content immediately and regenerate it after each request. If you’re happy to keep the content fresh for a while you can use the ttl
method alongside swr
.
#Immutable content
Sometimes you are generating content that you know will never change. This might be an asset that includes a hash in the URL, or other unchanging unique content. For these you can use the immutable
to cache the content for the year in both the CDN and the browser, adding the immutable
directive to the headers.
Careful
Be careful when using immutable
as it will cache the content for a year in the browser. If you need to change the
content you will need to change the URL or the content itself. It is best to restrict its use to cases where the URL
is guaranteed to be unique and unchanging, such as assets with a hash in the URL.
#Using the CacheHeaders
object with a framework
The CacheHeaders
object works perfectly with frameworks SSR functions. It can be used anywhere you can set response headers. It is a valid fetch Headers
object, so can be used with the Response
constructor. Some frameworks use a readonly Headers
object, where you need to set the headers in the response object directly. In this case you can use the copyTo
method to copy the headers to the response object:
#Troubleshooting
If you’re having trouble, the Cache-Status
header can be useful for debugging. It shows the status of the cache for the request, including whether the content was served from the cache or not, and whether it was stale or fresh. For more details see the cache debugging docs.
#Learn more
For more details and full API docs, see cdn-cache-control
on GitHub.