Skip to content

Cache-Control

Cache-Control headers tell browsers and CDNs how long to cache content. While Next.js manages server-side caching internally, you still need to set Cache-Control headers for static assets and API responses.

Without Cache-Control headers:

  • Browsers may cache assets for unpredictable durations
  • CDNs may not cache your content at all
  • Users may see outdated versions of your API responses

With proper Cache-Control headers, you control exactly how long assets are cached at each level.

app/api/posts/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
const posts = await db.post.findMany()
// Cache at CDN for 1 hour, browser for 2 minutes
return NextResponse.json(
{ data: posts },
{
headers: {
'Cache-Control': 'public, s-maxage=3600, max-age=120',
},
}
)
}
DirectiveMeaningExample
publicCacheable by anyone (CDN + browser)API responses
privateCacheable only by the browserPersonalized content
max-ageBrowser cache duration (seconds)max-age=3600 (1 hour)
s-maxageCDN/proxy cache duration (seconds)s-maxage=86400 (1 day)
no-cacheCheck with server before using cacheDynamic content
no-storeNever cacheSensitive data
stale-while-revalidateServe stale while fetching freshBest for performance
// Override Vercel's default CDN behavior
export async function GET() {
return NextResponse.json(
{ data },
{
headers: {
'Cache-Control': 'public, max-age=0, must-revalidate',
'CDN-Cache-Control': 'public, s-maxage=3600',
'Vercel-CDN-Cache-Control': 'public, s-maxage=3600',
},
}
)
}

Note: CDN-Cache-Control and Vercel-CDN-Cache-Control are specific to Vercel. Other CDN providers may use different mechanisms.

Next.js automatically optimizes caching for static assets in public/:

// Static files in public/ get optimal cache headers automatically
// public/favicon.ico → Cache-Control: public, max-age=31536000, immutable
ContentCache-ControlRationale
Static assets (JS, CSS)max-age=31536000, immutableHashed filenames, can cache forever
Imagesmax-age=86400, s-maxage=604800Cache 1 day browser, 1 week CDN
API responsesmax-age=60, s-maxage=300Short browser cache, longer CDN
User-specific dataprivate, max-age=0Never cache on CDN
SEO metadatapublic, max-age=3600Cache for 1 hour
  • Not setting any Cache-Control headers — Default behavior may not be optimal for your use case.
  • Confusing max-age and s-maxage — max-age is for browsers, s-maxage is for CDNs.
  • Using no-cache when you mean no-store — no-cache still caches (just revalidates first).
  • Setting immutable on non-hashed files — Only use immutable for files with content hashes in their filenames.
  • Use s-maxage for CDN caching and max-age for browser caching
  • Use stale-while-revalidate for API responses to serve fast fallbacks
  • Set immutable on versioned static assets (JS/CSS bundles)
  • Set private for user-specific API responses
  • Never cache sensitive data with public

Cache-Control headers give you fine-grained control over caching at the browser and CDN level. Use s-maxage for CDN duration, max-age for browser duration, and stale-while-revalidate for performance. Set immutable for versioned static assets.