Blog
TTFB benchmarks for real sites, how DNS, TCP, TLS and server processing add up, how to measure from several regions and how to alert on rising TTFB before an outage.
A good time to first byte is 0.8 seconds or less as experienced by real users, which is the threshold Google's web.dev guidance uses. Anything above 1.8 seconds counts as poor. For the part your server actually controls, the processing time after the TLS handshake, a practical target is under 200 ms for cached pages and under 500 ms for dynamic ones. The total always depends on where you measure from, because distance adds network round trips before your server starts working.
This guide breaks TTFB into its parts, shows how to measure each one with curl, explains why the same page has different TTFB in different regions and how to use a slowly rising TTFB as a warning sign days before something falls over.
There is no single correct number, because people mean different things by TTFB. Three reference points are useful:
The first two benchmarks describe user experience. The third describes your backend. When someone asks whether their TTFB is good, the honest answer is usually: check the server share first, because the network share is mostly physics.
On a fresh connection to an HTTPS site, the first byte can only arrive after several steps have finished in sequence:
This gives you a network floor you cannot optimise away in code. With a round-trip time of 150 ms between a probe and your origin, TCP plus TLS 1.3 plus the request itself already cost roughly three round trips, about 450 ms, before a single millisecond of server processing. The same server measured from a probe 10 ms away shows a fraction of that.
That is why a TTFB of 600 ms can be excellent from one continent and alarming from another. Comparing raw TTFB numbers across locations without splitting them into phases leads to wrong conclusions.
curl exposes timing variables for each phase. They are cumulative, measured from the start of the request:
curl -o /dev/null -s -w '\ndns: %{time_namelookup}\nconnect: %{time_connect}\ntls: %{time_appconnect}\nttfb: %{time_starttransfer}\ntotal: %{time_total}\n' https://example.com/An example output might look like this:
dns: 0.012
connect: 0.041
tls: 0.089
ttfb: 0.312
total: 0.330Read it as differences, not absolute values:
Subtract one round trip (about the TCP time) from the last value and you have an estimate of pure server time, here around 190 ms. That is the number to track over time. If you want the breakdown inside the server too, emit a Server-Timing header from your application:
Server-Timing: db;dur=84, render;dur=61, cache;desc="miss"Browser developer tools display these values next to the request timing, so you can see whether a slow first byte is the database, the template or a cache miss without attaching a profiler.
Two rules make measurements comparable. First, decide whether you measure a cold or warm connection: curl opens a new connection every time, a browser reusing a connection skips DNS, TCP and TLS entirely. Second, know whether the URL is served from a CDN cache. A cached HTML page hides the origin completely, so for backend health measure an uncached endpoint that touches the database, for example a health route. The guide on monitoring API endpoints with health checks and latency thresholds covers how to design such an endpoint.
A single measurement from your laptop tells you very little. Run the same request repeatedly from several locations and look at percentiles instead of averages, because averages hide the slow tail that users actually notice:
for i in $(seq 1 20); do
curl -o /dev/null -s -w '%{time_starttransfer}\n' https://example.com/health
done | sort -n | awk '{a[NR]=$1} END {print "p50:", a[int(NR*0.5)], "p95:", a[int(NR*0.95)]}'Run this from a few machines in different regions, or use monitoring probes that already sit there. Then compare the phases, not the totals:
Regional TTFB data is also a good sanity check for any claim like "the site is fast". It is fast from somewhere. The question is whether it is fast from where your customers are.
Most outages are not a switch flipping from fine to broken. Before a service stops answering, it usually answers more slowly: queues fill, connection pools run dry, a database query degrades as a table grows. TTFB is where that shows up first, because the server processing phase stretches long before requests start timing out.
The useful signal is not whether one TTFB value is good, but whether the server share of TTFB is moving away from its own baseline.
Typical patterns and what they tend to mean:
A server that went from 150 ms to 900 ms of processing time over a week is still up by every uptime check. It is also one traffic spike away from timeouts.
TTFB alerts are early warnings, so they should behave differently from down alerts. A workable setup:
If slow-response alerts start firing for blips, the same techniques as for down checks apply. The article on reducing false positive alerts with retries, realistic timeouts and split thresholds goes through them in detail.
You can collect all of the above with cron and curl on a few small servers, but you then also own the storage, the percentiles and the alerting. Most uptime monitoring tools already make an HTTP request from several locations on every check, which is the same request that produces a TTFB value.
m0nitor is one option: its HTTP(S) and other monitor types run on a fixed interval from distributed probes in several regions, and an outage is only confirmed when multiple locations agree, which keeps single-route network blips out of your alerts. Definitions for TTFB, SLO, error budget and related terms are collected in the monitoring glossary. Whatever tool you use, the approach stays the same: regional measurements, percentiles, a baseline and a warning threshold that sits below the point where checks fail.
Yes, for most sites 200 ms is a good time to first byte. As a server processing time it is in line with a cached or well-optimised page, and as a total including DNS, TCP and TLS it usually means the client is close to the server or to a CDN edge.
TTFB ends when the first byte of the response arrives. Total response time ends when the last byte arrives, so it additionally includes transferring the full body. For small HTML or JSON responses the two are close, for large downloads the transfer dominates.
For cacheable content, yes, because the response comes from a nearby edge and skips the origin entirely. For uncached, dynamic requests a CDN mainly shortens connection setup by terminating TCP and TLS closer to the user, while server processing time at the origin stays the same.
Usually because of distance: each round trip for TCP, TLS and the request takes longer the further the client is from the server. If the server processing share also differs between regions, look at CDN cache state, regional replicas and load balancer routing.