Page speed, redirects, Core Web Vitals
Fix TTFB slow: cut Time to First Byte under 800ms
Time to First Byte above 800ms starves every other Core Web Vital. Move rendering to the edge, cache aggressively, and tune origin response times to unblock LCP.
What's happening
Time to First Byte measures how long the browser waits between starting a request and receiving the first byte of the document response. Web.dev's recommended thresholds are under 800ms for good and 800-1800ms for needs improvement, though for static content under 200ms is achievable. TTFB is upstream of every other Core Web Vital — if your TTFB is 1.5s, your LCP can never be better than 1.5s plus rendering time.
Chrome DevTools' Network panel exposes the TTFB breakdown under "Timing": queueing, stalled, DNS lookup, initial connection, SSL, request sent, waiting for server response, content download. The "Waiting for server response" segment is the actual server processing time. WebPageTest and SpeedCurve report TTFB explicitly, and the web-vitals.js library exposes it via onTTFB().
Slow TTFB usually comes from one of three places: slow origin (uncached database queries, render-blocking SSR work, distant origin from the user), missing CDN cache hits forcing every request to the origin, or DNS/connection setup overhead on cold visits.
Why it matters
TTFB doesn't directly factor into Google's ranking algorithm but it constrains LCP, which does. A 2-second TTFB makes a sub-2.5s LCP mathematically impossible. Pages with TTFB above 1.8s consistently fail Core Web Vitals at the 75th percentile in CrUX.
User-facing, slow TTFB shows up as a long blank-screen period before any content appears. This is the worst kind of slow because the user has nothing to look at while waiting — no progress indication, no skeleton, no header. Bounce rates spike on routes where TTFB exceeds 2 seconds, especially on mobile data.
Common causes
- Server-rendered pages that wait for slow database queries before streaming HTML.
- Origin server located far from the user without an edge cache in between.
- Cache-Control headers missing or set to no-store, forcing every request to the origin.
- TLS handshake overhead on cold connections without HTTP/3 or 0-RTT.
- Cold-start latency on serverless functions in regions with low traffic.
- Heavy middleware (auth checks, A/B test cookie reads) running before any cache lookup.
- Synchronous N+1 queries during SSR data fetching.
Detect this on your site
Run a quick scan with the Speed Test. The tool surfaces this exact issue with the records and context needed to apply the fix below.
Open Speed TestHow to fix it
- 1
Measure TTFB across regions
Run WebPageTest from multiple regions (US East, EU West, APAC) to see if TTFB varies. A flat-globally-slow TTFB points to origin performance; a region-dependent TTFB points to missing CDN coverage. Calibre or SpeedCurve schedule this for you.
- 2
Cache HTML at the edge
If your pages don't need per-request personalization, set Cache-Control: public, s-maxage=60, stale-while-revalidate=600 and serve through a CDN. Vercel and Cloudflare Pages automatically cache responses with these headers. Static-then-dynamic patterns work even for logged-in pages via shell-with-island hydration.
- 3
Use streaming SSR with React Server Components
Don't wait for slow data before sending the first byte. Stream the shell immediately and Suspense around slow data fetches. Next.js App Router does this by default — TTFB becomes shell-render time (often under 100ms), not full-page render time.
- 4
Move dynamic logic to edge runtime
If your origin is a single region but users are global, run the dynamic parts at the edge. Vercel Edge Functions, Cloudflare Workers, or Fastly Compute@Edge run within 50ms of every user. Keep heavy database access on the origin and use the edge for routing, auth, and personalization.
- 5
Optimize database queries on the critical path
Profile the slowest queries on your hot routes with EXPLAIN ANALYZE. Add covering indexes for queries that read from indexed columns. Cache query results in Redis or Upstash with short TTLs (5-30s) — even a 5-second cache halves origin load on bursty traffic.
- 6
Enable HTTP/3 and 0-RTT
HTTP/3 over QUIC removes head-of-line blocking and supports 0-RTT resumption for repeat visitors. Cloudflare, Fastly, and CloudFront all enable HTTP/3 with one toggle. The connection-setup portion of TTFB drops from 200ms to under 50ms on 4G mobile.
- 7
Reduce middleware overhead
Middleware runs before every request. Move auth-not-required routes out of the middleware matcher in Next.js. For routes that do need auth, use Edge Middleware with cached session lookups instead of synchronous database calls.
Example
// Next.js: cache HTML at the edge with stale-while-revalidate
export async function GET() {
return new Response(html, {
headers: {
"Cache-Control": "public, s-maxage=60, stale-while-revalidate=600",
"Content-Type": "text/html; charset=utf-8",
},
});
}
// App Router: stream the shell, suspend slow data
export default function Page() {
return (
<main>
<Header />
<Suspense fallback={<Skeleton />}>
<SlowProductList />
</Suspense>
</main>
);
}Edge caching plus streaming SSR for sub-100ms shell TTFB.
Frequently asked
Under 200ms for cached responses under 800ms for dynamic. If you can't hit 800ms uncached your data layer is the bottleneck — profile the queries not the framework.
Yes. The browser-reported TTFB is from request initiation to first byte received which includes DNS TCP and TLS. The server-side processing portion is the Waiting for server response timer in DevTools.
Yes for mobile-heavy traffic. HTTP/3's 0-RTT resumption and QUIC transport cut connection-setup time substantially on flaky networks. Cloudflare and Fastly enable it with a checkbox — there's no downside.
Related fixes
Page speed, redirects, Core Web Vitals
Fix LCP too slow: get Largest Contentful Paint under 2.5s
Page speed, redirects, Core Web Vitals
Fix FCP slow: get First Contentful Paint under 1.8s
Page speed, redirects, Core Web Vitals
Use a CDN to cut latency and offload origin traffic
Page speed, redirects, Core Web Vitals
Set Cache-Control headers to enable browser and CDN caching