Fix Slow TTFB on Vercel โ Edge Functions vs Serverless
Updated April 2026
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