Updated April 2026

Cache-Control Headers on Vercel — vercel.json Configuration

Quick Answer

Add a headers array to vercel.json. Use "/static/(.*)" as the source for static assets with max-age=31536000, immutable. Use "/(.*)" with no-cache for HTML. API routes in /api/ should use no-store.

Vercel serves static files with its own default caching but does not set Cache-Control headers on your HTML pages or API routes. Configure them explicitly in vercel.json.

Generate Cache-Control headers →

vercel.json — complete configuration

{
  "headers": [
    {
      "source": "/static/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" },
        { "key": "Vary", "value": "Accept-Encoding" }
      ]
    },
    {
      "source": "/_next/static/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    },
    {
      "source": "/api/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "no-store" }
      ]
    },
    {
      "source": "/(.*).html",
      "headers": [
        { "key": "Cache-Control", "value": "no-cache" }
      ]
    },
    {
      "source": "/((?!_next/static|static).*)",
      "headers": [
        { "key": "Cache-Control", "value": "no-cache" }
      ]
    }
  ]
}

Next.js — cache in route handlers

// app/api/data/route.ts
export async function GET() {
  const data = await fetchData();
  return Response.json(data, {
    headers: {
      // Public data — cache at CDN
      'Cache-Control': 'public, max-age=60, stale-while-revalidate=300',
    },
  });
}

// Authenticated route — never cache
export async function GET(request: Request) {
  return Response.json(userData, {
    headers: { 'Cache-Control': 'no-store' },
  });
}

Next.js App Router — fetch cache

// Force revalidation every 60 seconds
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 60 },
});

// Never cache
const data = await fetch('https://api.example.com/user', {
  cache: 'no-store',
});

// Cache indefinitely (until manual revalidation)
const data = await fetch('https://api.example.com/config', {
  cache: 'force-cache',
});

Edge Functions

// Edge function — set Cache-Control in response
export const config = { runtime: 'edge' };

export default function handler(req: Request) {
  return new Response(JSON.stringify({ data: 'hello' }), {
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=60, stale-while-revalidate=300',
    },
  });
}

Check your Vercel cache headers

curl -sI https://yoursite.vercel.app/api/data | grep -i "cache\|x-vercel"

# x-vercel-cache: HIT means Vercel's edge cached the response
# x-vercel-cache: MISS means it hit your function
Audit your live cache headers → EdgeFix