billing hacks
CachingCDN Cache

CDN Cache

Cache API responses at the edge. When this works, your function doesn't run at all.

TL;DR

Add Cache-Control: public, s-maxage=300 to cacheable responses. When CDN cache hits, your function doesn't run at all - zero cost. Watch out for cookies/headers that bust the cache.

The outermost cache layer. Vercel's CDN checks for a cached response before calling your function.

User Request → CDN: "Have I seen this?"
  → Yes: Return cached (FREE)
  → No: Call function, cache, return

Setting Cache Headers

app/api/products/route.ts
export async function GET() {
  const products = await db.products.findMany();

  return Response.json(products, {
    headers: {
      'Cache-Control': 'public, s-maxage=300',
      'Stale-While-Revalidate': '3600',
    },
  });
}

Key Headers

HeaderPurpose
s-maxage=300Cache at CDN for 5 minutes
stale-while-revalidate=3600Serve stale while fetching fresh
publicAllow CDN caching
privatePrevent CDN caching (user data)

The Auth Gotcha

Reading cookies or auth headers busts the cache:

// ❌ Won't cache - reads cookies
export async function GET(request: Request) {
  const session = request.cookies.get('session');
  // ...
}

Fix: Separate public and private endpoints:

app/api/products/public/route.ts
export async function GET() {
  return Response.json(await getPublicProducts(), {
    headers: { 'Cache-Control': 'public, s-maxage=300' },
  });
}

// app/api/products/mine/route.ts - Not cached
export async function GET(request: Request) {
  const user = await getUser(request);
  return Response.json(await getUserProducts(user.id));
}

Debugging

Check Vercel Dashboard → Analytics → Cache for hit rates.

Low hit rate? You're probably reading cookies/headers somewhere.


Next: Route Cache for caching entire pages.

On this page