Guide for controlling caching on Netlify's CDN. Use when configuring cache headers, setting up stale-while-revalidate, implementing on-demand cache purge, or understanding Netlify's CDN caching behavior. Covers Cache-Control, Netlify-CDN-Cache-Control, cache tags, durable cache, and framework-specific caching patterns.
76
93%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Static assets are cached automatically:
max-age=0, must-revalidate)Dynamic responses (functions, edge functions, proxied) are not cached by default. Add cache headers explicitly.
Three headers control caching, from most to least specific:
| Header | Who sees it | Use case |
|---|---|---|
Netlify-CDN-Cache-Control | Netlify CDN only (stripped before browser) | CDN-only caching |
CDN-Cache-Control | All CDN caches (stripped before browser) | Multi-CDN setups |
Cache-Control | Browser and all caches | General caching |
// Cache at CDN for 1 hour, browser always revalidates
return new Response(body, {
headers: {
"Netlify-CDN-Cache-Control": "public, s-maxage=3600, must-revalidate",
"Cache-Control": "public, max-age=0, must-revalidate",
},
});
// Stale-while-revalidate (serve stale for 2 min while refreshing)
return new Response(body, {
headers: {
"Netlify-CDN-Cache-Control": "public, max-age=60, stale-while-revalidate=120",
},
});
// Durable cache (shared across edge nodes, serverless functions only)
return new Response(body, {
headers: {
"Netlify-CDN-Cache-Control": "public, durable, max-age=60, stale-while-revalidate=120",
},
});For fingerprinted files (hash in filename):
# netlify.toml
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"Tag responses for selective cache invalidation:
return new Response(body, {
headers: {
"Netlify-Cache-ID": "product,listing",
"Netlify-CDN-Cache-Control": "public, s-maxage=86400",
},
});Purge by tag:
import { purgeCache } from "@netlify/functions";
export default async () => {
await purgeCache({ tags: ["product"] });
return new Response("Purged", { status: 202 });
};Purge entire site:
await purgeCache();Responses with Netlify-Cache-ID are excluded from automatic deploy-based invalidation — they must be purged explicitly.
Customize what creates separate cache entries:
return new Response(body, {
headers: {
"Netlify-Vary": "cookie=ab_test|is_logged_in",
// Other options: query=param1|param2, header=X-Custom, country=us|de, language=en|fr
},
});ISR uses Netlify's durable cache automatically (runtime 5.5.0+). revalidatePath and revalidateTag trigger cache purge.
Full control over cache headers in server routes. Set Netlify-CDN-Cache-Control in responses for CDN caching.
Default Nitro preset handles caching. ISR-style patterns use routeRules with swr or isr options.
Static assets are cached by default. API responses from Netlify Functions need explicit cache headers.
Check the Cache-Status response header:
HIT — served from cacheMISS — generated freshREVALIDATED — stale content was revalidatedNetlify-Vary headers across responsescffaf74
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.