Help us improve
Share bugs, ideas, or general feedback.
From netlify
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.
npx claudepluginhub robinebers/converted-plugins --plugin netlifyHow this skill is triggered — by the user, by Claude, or both
Slash command
/netlify:netlify-cachingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Static assets** are cached automatically:
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
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 responses