FDEInterviews logo
System Design & Production Engineering / 10
mediumVercelRetool

A customer's Next.js site has a 3-second TTFB. Diagnose it and walk me through fixes, and how you'd prove each one worked.

Vercel's signature triage question. 3s TTFB is almost never 'the server is slow', it's a rendering-strategy problem with four classic causes, and the rubric rewards proving each fix with a measurement, not vibes.

Updated Aug 2026 · Grounded in real Forward Deployed Engineer interview loops and written to a senior-engineer editorial bar.

TL;DR: TTFB measures server render path, not client assets, so start with the x-vercel-cache header (HIT vs MISS) and a curl -w breakdown before theorizing, then attack the four classic causes in order: per-request SSR, blocking data fetches, cold start / region mismatch, and middleware on the hot path, proving each fix with a measurement.

How to approach it

TTFB is time until the first byte of HTML, so client-side bundle size and images are off the table. This is server-side render path, by construction. Strong candidates segment the render path, name the rendering strategies (SSR/ISR/SSG/edge), and attach a measurement to every proposed fix, because the question explicitly asks you to prove it.

A strong answer

Measure before touching anything. Establish where the 3 seconds go. A curl -w timing breakdown separates DNS/TLS/connect from server think-time. Check whether it's all pages or some, all users or cold-cache only, p50 or tail. The x-vercel-cache header (HIT/MISS/STALE) tells you instantly whether responses are being served from the edge cache or rendered on every request, the single highest-yield check.

rendering diagram…

The four classic causes, in observed-frequency order:

  1. SSR on every request when the page doesn't need it. A page using getServerSideProps (or dynamic rendering in the App Router via cookies(), headers(), or cache: 'no-store') renders per-request. If content changes hourly, that's waste. Fix: static generation with ISR (revalidate: 3600) so pages serve from the edge cache and regenerate in the background. Proof: x-vercel-cache: HIT and TTFB dropping to tens of milliseconds.
  2. Slow data fetches blocking the render. The render awaits a 2.5s backend/CMS/DB call. Fix: cache the fetch (fetch revalidation, Redis), parallelize sequential awaits, or stream. Send the shell immediately and <Suspense> the slow part so TTFB decouples from the slowest query. Proof: server timing logs around each fetch, before and after.
  3. Cold starts and region mismatch. Serverless cold starts add hundreds of ms to seconds (worse with heavy dependencies). A function in us-east-1 calling a database in eu-west adds roughly 100ms per round trip, and ORMs that make several sequential queries multiply it: a page whose render runs six sequential queries (session, user, org, feature flags, content, related items, a completely ordinary ORM pattern) pays 6 x 100 = 600ms of pure geography before a single row is processed. The tell in the logs is distinctive and worth knowing cold: every query reports almost exactly the same latency, ~100ms, regardless of complexity, because the wire dominates the work. Locally the same queries run in 1-2ms and the app feels instant, which is why this bug ships. Fix: co-locate compute and data, batch or parallelize the queries, trim the bundle, consider edge runtime for thin pages. Proof: compare cold vs warm invocations, log per-query latency and watch the flat-100ms signature disappear.
  4. Middleware or auth on the hot path. Middleware running on every request doing a fetch to an auth service or feature-flag API. Fix: JWT verification locally instead of an introspection round trip, cache flag configs. Proof: middleware execution time in logs.

Sequence them: ISR/caching is usually the 10x win for content-like pages, data-layer fixes are the win for dynamic ones. State the decision rule: "Does this page need per-request data? If not, don't render it per request."

What interviewers probe next

  • "The page is personalized, you can't just cache it." Split it: static shell served from edge, personalize client-side or stream the personalized fragment, or cache-by-segment with a small key (locale, plan tier) instead of per-user.
  • "How do you keep it from regressing?" TTFB budget in CI/synthetic monitoring, alert on cache hit-ratio drops, dashboard real-user TTFB by region.
  • "When does edge rendering actually help?" Thin, latency-sensitive pages without heavy Node dependencies and with data accessible from the edge. It does nothing if the bottleneck is a single-region database.

Key takeaways

  • Check x-vercel-cache HIT vs MISS first; it decides the entire branch of your investigation.
  • The four causes in frequency order: per-request SSR, blocking fetch, cold start / region mismatch, middleware on the hot path.
  • Every proposed fix carries its own proof metric, because the question asks you to prove it, not assert it.

Common mistakes

Talking about image optimization, bundle splitting, or lazy loading, all post-first-byte, and the fastest way to show you don't know what TTFB measures. Saying "add caching" without distinguishing SSG/ISR/SSR or knowing what makes a route dynamic. Proposing fixes with no verification step when the question literally asks for proof. And skipping measurement, picking a favorite fix before knowing whether the time is in cold start, data fetch, or render.

That one was free — and so are 10 answers per topic without an account. Signing in doubles that to 20, opens the Plus lessons in the courses, and remembers which topics you keep getting wrong.no card · Google sign-in · nothing to cancel
HOW DID IT GO?
0
READING SIGNED OUT

Signing in doubles your free answers, from 10 to 20 per topic, and the site starts remembering you: mastery per topic, bookmarks, and a next-focus recommendation. Free, no card.

Sign in free
UP NEXT ON YOUR JOURNEY
FEDITOR'S NOTE

The fastest way to fail this is to start talking about image optimization or bundle splitting, since both happen after the first byte and signal you don't know what TTFB measures. The near-certain follow-up is 'the page is personalized, so you can't just cache it,' where the strong move is splitting a static edge shell from a streamed personalized fragment rather than giving up on caching. Reach for the x-vercel-cache header early; HIT versus MISS is the single highest-yield check before you theorize about anything.

DISCUSSION · 0

No comments yet — be the first to share your approach.