Today we’re rolling out a set of new features that’ll greatly enhance the experience of using Netlify’s Edge Caching with Netlify Functions or external dynamic backends.
Our platform now supports handling any custom Cache-Control
instructions and Netlify-CDN-Cache-Control
instructions at the Edge, and we’ve added full support for the stale-while-revalidate
standard, with automatic cache purges whenever you deploy.
As the first modern front-end cloud platform, one of Netlify’s core inventions was to integrate CI/CD, Hosting and Edge Delivery into one coherent workflow with zero friction from code to URL.
We were the first to make edge caching and cache invalidation completely transparent to developers, based on the idea of static site generation as the contract that would always tell our platform when something had changed and should be invalidated.
Until now, however, we had relatively coarse support for controlling the edge cache behavior when using Serverless Functions or when proxying from our edge to an external application.
Today, we’re changing that and making Netlify best in class for caching dynamic functions with predictable and integrated cache purging and revalidation.
How do these new edge caching features work?
Netlify now fully supports the Cache-Control
, CDN-Cache-Control
and Netlify-CDN-Cache-Control
headers, allowing you to send different cache instructions to your end user’s browser (Cache-Control
), Netlify’s Edge (Netlify-CDN-Cache-Control
), or any standards compliant CDN sitting in-between Netlify and your end user (CDN-Cache-Control
).
Here’s an example of a Function that will be cached on Netlify’s edge nodes until a new deploy goes live:
import type { Handler, HandlerEvent, HandlerContext } from "@netlify/functions";
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
console.log("Regenerating String")
const headers = {
"Content-Type": "text/plain",
"Cache-Control": "public, max-age=0, must-revalidate", // Tell browsers to always revalidate
"Netlify-CDN-Cache-Control": "public, max-age=31536000, must-revalidate", // Tell Edge to cache asset for up to a year
}
return {
statusCode: 200,
body: "Hello, World!",
headers
}
};
export { handler };
When you access this function repeatedly, you’ll see it getting cached on Netlify Edge. If you redeploy, the cache will be invalidated and the function will be called again.
Netlify Edge nodes can also handle Conditional Get Requests for content that you want to avoid regenerating after a new deploy. Here’s a small tweak to the function above:
import crypto from 'crypto';
import type { Handler, HandlerEvent, HandlerContext } from "@netlify/functions";
const body = "Hello, World";
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
const etag = `"${crypto.createHash("md5").update(body).digest("hex")}"`;
const headers = {
"Content-Type": "text/plain",
"Cache-Control": "public, max-age=0, must-revalidate", // Tell browsers to always revalidate
"Netlify-CDN-Cache-Control": "public, max-age=31536000, must-revalidate", // Tell Edge to cache asset for up to a year
"ETag": etag,
}
if (event.headers["if-none-match"] === etag) {
return { statusCode: 304, headers }
}
console.log("Regenerating String");
return { statusCode: 200, body, headers }
};
export { handler };
In this case, if you deploy this function and visit it from an edge node, the string “Hello, World” will get cached on Netlify Edge. If you do a new deploy with no changes to the function, then the first time you visit the function, our Edge Node will send a request back to the function with an If-None-Match header. The function will detect this and send back an empty 304 response, telling Netlify Edge to go ahead and keep using the response from the cache.
SWR (stale-while-revalidate)
Another powerful caching technique is the “stale-while-revalidate” pattern. This pattern tells our Edge to serve a cached response even when it’s stale, but revalidate it in the background without requiring the client to wait for the result.
Here’s an example of a function using stale while revalidate:
import type { Handler, HandlerEvent, HandlerContext } from "@netlify/functions";
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
const resp = await fetch('https://reqres.in/api/news')
const body = await resp.json()
return {
statusCode: 200,
body: JSON.stringify({
time: new Date(),
news: body
}),
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=0, must-revalidate", // Tell browsers to always revalidate
"Netlify-CDN-Cache-Control": "public, max-age=0, stale-while-revalidate=31536000", // Tell Edge to cache asset for up to a year
}
}
};
export { handler };
This function wraps a public Rest API that could take some time to get back and might very well be subject to strict rate limits.
If you deploy this and visit the function, the first request will be served from the function, stored in the Edge Cache, and then when you refresh the browser, you’ll notice that the following responses are instant and served out of the Netlify Edge.
If this endpoint had thousands of concurrent visitors, after the initial function load, each of them would keep getting a cached response, while our edge node would continuously revalidate the cached object in the background by sending one request at a time to the serverless function, protecting the underlying API from traffic spikes and delivering instant responses to every user.
Demo repository
We’ve created a demo repository where you can explore these patterns yourself and see them in action on Netlify’s edge at the demo site.
What’s next for edge caching on Netlify?
We’re excited to deliver this next level of control over Netlify’s edge cache to all of you, and have a roadmap of improvements to our core platform primitives that we’re stoked to share with the world over the next months.
Expect more control over edge caching patterns, better experiences writing Netlify functions and more options for persisting responses outside of Builds and Atomic Deploys.