Time to First Byte (TTFB)
How long the server took to send the first byte, and why it's the floor every other metric sits on.
TTFB measures the time from navigation start to the first byte of response landing in the browser. Nothing visual can happen before it arrives.
TTFB is not a Core Web Vital. It is the diagnostic metric to check first for a slow LCP or FCP.
What's a good TTFB?
TTFB is reported in milliseconds. Track the 75th percentile.
| TTFB at p75 | Rating |
|---|---|
| ≤ 800 ms | Good |
| ≤ 1,800 ms | Needs improvement |
| > 1,800 ms | Poor |
TTFB under 200 ms is excellent.
What goes into TTFB
TTFB is the sum of:
- Redirects. Each
301/302is a separate roundtrip. - DNS lookup. First request to your origin per session.
- TCP + TLS handshake. One roundtrip each on a fresh connection (HTTP/2 reuses connections; HTTP/3 collapses them further).
- Request → server processing → response. The time the server spends thinking before it can start streaming.
- First byte arrival. Network propagation from server to client.
If your TTFB is dominated by step 4, the fix is in your application. If it's dominated by 1–3 or 5, the fix is in your network and edge posture.
When TTFB is reported
TTFB is captured from the Navigation Timing API as responseStart - fetchStart. The tracker includes it with every page-view that has a new navigation entry.
TTFB behind 103 Early Hints
On sites behind 103 Early Hints (Shopify storefronts, Cloudflare) TTFB regularly reads below the server's own Server-Timing phases for the same pageview. Both are right. responseStart marks the first byte of the first response, and with Early Hints that is the interim 103 the edge answers in about one round trip; your server's real response, and the Server-Timing header on it, comes later.
To tell them apart, fastmon also stores ttfb_final: the browser's finalResponseHeadersStart, the first byte of the final response. The tracker reads it with the same activation adjustment and 60-second cap as TTFB (wire key frs), and the collector drops a value below ttfb. Reading the pair:
ttfb_final | Meaning |
|---|---|
empty (NULL) | The browser does not report it (Safari, Firefox; Chromium before 115), or the pageview was a soft navigation or a bfcache restore. |
equal to ttfb | No interim response; the first byte was the real one. |
above ttfb | Early Hints active. The difference is the edge's head start, and ttfb_final is the value the server's edge_dur + backend_dur has to fit into. |
TTFB itself stays responseStart, so it keeps comparing to CrUX and the web-vitals field data. ttfb_final is a row field on POST /analytics/beacons, part of the visitor pageview detail, and an aggregatable metric (avg, p50 to p99).
In the dashboard, the bars of Where the time goes run to the final first byte, and a mark shows where the browser counted the first byte. The response breakdown draws the head start as its own "Early Hints" row inside the wait and starts the transfer bar at the final response headers. In the Explorer, ttfb_final is a metric, a column, and a drawer field, shown in the pageview drawer only when it differs from TTFB.
How to see TTFB in fastmon
The TTFB page sits under Performance → TTFB. On top are the value with its rating and trend, the pageviews by rating, and a few sentences on where the time goes.
- Where the time goes: connection, server, the way in between, and the unmeasured wait of pageviews without Server-Timing. One bar each for all pageviews and for those answered by the CDN, the origin cache, or neither.
- Server split: what Server-Timing reports, as edge, origin, database, cache, search, HTTP calls, own code, and templates. Without the header you see one block and a link to the setup guide.
- History: the selected percentile against the rating zones, one line per phase, or one line per page type, beside the distribution. The maximize icon fills the window with the chart.
- Where the TTFB is slow: page types, pages, CDN status, login state, origin hosts, and more, ranked by slow pageviews with the split in every row.
- Cache and origin: each cache layer with and without a hit, its hit rate, and the backend time per origin host. A host unlike its peers is called out.
- Compare: works as on the LCP page, see Compare with an earlier window.
How to improve a slow TTFB
- Cache HTML at the edge. A static or near-static page should be served from a CDN cache, not your origin.
- Move closer to your visitors. Multi-region origins or a well-distributed edge layer cut RTT for everyone outside your primary region.
- Profile the server response. Use APM, server logs, or a server-timing header to find what's slow. Common culprits: a slow database query, a synchronous third-party call, or server-rendered React hydration that traverses too much data.
- Reduce redirect chains. Every redirect adds a full roundtrip.
http://→https://→www.→ final URL is three roundtrips before the browser even gets the HTML. - Use HTTP/2 or HTTP/3. Older origins still negotiate HTTP/1.1.
Common surprises
- A fast TTFB on Vercel/Netlify can hide a slow origin. The edge caches the response; cache misses might be 10× slower. Check your cache hit rate alongside TTFB.
- TTFB doesn't include client-side render time. A page with a fast TTFB and a slow LCP is shipping a lot of work to the browser, not the server. SSR doesn't help if hydration is the bottleneck.
- Service-worker caches affect TTFB the visitor experiences. If your service worker serves the navigation, TTFB can be near zero on repeat visits.
What's next
- FCP: TTFB's downstream neighbor.
- LCP: what slow TTFB usually breaks.
- Web Vitals overview.