Skill v1.0.0
Trusted Publisher100/100version: "1.0.0" name: netlify-caching description: 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.
Caching on Netlify
Default Behavior
Static assets are cached automatically:
- CDN: cached for 1 year, invalidated on every deploy
- Browser: always revalidates (
max-age=0, must-revalidate) - No configuration needed
Dynamic responses (functions, edge functions, proxied) are not cached by default. Add cache headers explicitly.
Cache-Control Headers
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 |
Common Patterns
// Cache at CDN for 1 hour, browser always revalidatesreturn 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",},});
Immutable Assets
For fingerprinted files (hash in filename):
# netlify.toml[[headers]]for = "/assets/*"[headers.values]Cache-Control = "public, max-age=31536000, immutable"
Cache Tags and On-Demand Purge
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.
Cache Key Variation
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},});
Framework-Specific Caching
Next.js
ISR uses Netlify's durable cache automatically (runtime 5.5.0+). revalidatePath and revalidateTag trigger cache purge.
Astro / Remix
Full control over cache headers in server routes. Set Netlify-CDN-Cache-Control in responses for CDN caching.
Nuxt
Default Nitro preset handles caching. ISR-style patterns use routeRules with swr or isr options.
Vite SPA
Static assets are cached by default. API responses from Netlify Functions need explicit cache headers.
Debugging
Check the Cache-Status response header:
HIT— served from cacheMISS— generated freshREVALIDATED— stale content was revalidated
Constraints
- Basic auth disables caching for the entire site
- Durable cache is serverless functions only (not edge functions)
- Same URL must return identical
Netlify-Varyheaders across responses - Deploy invalidation is scoped to deploy context (production vs preview)