Performance

Fix Slow TTFB on Vercel โ€” Edge Functions vs Serverless

Updated April 2026

Reading this article? Verify your fix in real-time. Run a live PageSpeed audit โ€” SpeedFixer โ†’

TTFB over 600ms is almost always a server problem, not a frontend problem. On Vercel, the main cause is cold starts on serverless Node.js functions. Moving to Edge Runtime eliminates them.

Diagnose first

Open DevTools โ†’ Network โ†’ click your document request โ†’ Timing tab. If TTFB (Waiting for server response) is high, the problem is server-side. If TTFB is fine but the page is slow, the problem is rendering or resources.

Cause 1 โ€” Serverless cold starts

Vercel serverless functions spin down after inactivity. The first request after a cold period takes 500-2000ms extra for the container to start.

// Move to Edge Runtime โ€” no cold starts, runs globally
// app/api/data/route.ts (App Router)
export const runtime = 'edge';

export async function GET(request: Request) { return Response.json({ data: 'fast' });
}

Edge Runtime has limitations: no Node.js APIs, no filesystem access, smaller bundle size limit. If you need Node.js APIs, use edge middleware for the fast parts and defer heavy work to serverless.

Cause 2 โ€” No caching

If your function fetches from a database or external API on every request, add caching at the function level:

// App Router โ€” cache at the fetch level
export async function GET() { const data = await fetch('https://api.example.com/data', { next: { revalidate: 60 } // cache for 60 seconds }); return Response.json(await data.json());
}

// Or use unstable_cache for fine-grained control
import { unstable_cache } from 'next/cache';

const getCachedData = unstable_cache( async () => fetchFromDB(), ['data-cache-key'], { revalidate: 3600 }
);

Cause 3 โ€” Function not deployed at the right region

Serverless functions deploy to a single region by default. If your users are in Europe and your function is in US East, add 100-200ms of latency.

// vercel.json โ€” set function region
{ "functions": { "api/**/*.js": { "regions": ["fra1", "lhr1"]  // Frankfurt, London } }
}

Cache static pages at the CDN

For pages that do not change per user, set Cache-Control to let Vercel's CDN serve them without hitting the function:

// Next.js โ€” set cache headers in route
export async function GET() { return new Response(JSON.stringify({ data: 'ok' }), { headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400', }, });
}
Run a live PageSpeed audit โ†’ SpeedFixer
Visualise your Cache-Control header โ†’ Cache-Control Simulator