What Is TTFB? How to Measure It and Bring It Down
Time to First Byte (TTFB) is the wait between a browser asking for a page and the first byte of the answer arriving. Nothing renders until that byte shows up, so a slow TTFB delays everything after it: HTML parsing, CSS, images, and every Core Web Vital. This guide breaks TTFB into its parts, shows how to measure each one with a single curl command, and walks through the server-side fixes that move the number.
What TTFB actually includes
TTFB is not "how long your PHP code runs". It is the sum of several sequential phases, and each has different causes and different fixes:
- DNS lookup — turning
example.cominto an IP address. Usually fast when cached, occasionally slow on a cold cache or a sluggish DNS provider. - TCP connect — the three-way handshake with the server. Roughly one network round trip, so it grows with physical distance.
- TLS handshake — negotiating encryption for HTTPS. One extra round trip with TLS 1.3, two with TLS 1.2, plus certificate and key-exchange work.
- Request sent and server processing — the server receives the request, routes it, runs application code, queries the database, renders the response and starts sending it.
- First byte travels back — another half round trip across the network.
Redirects add a full extra cycle per hop. If http://example.com redirects to https://example.com and then to https://www.example.com, the visitor pays for DNS, connect, TLS and processing up to three times before the real page even starts. Web.dev's definition of TTFB for real users explicitly includes redirect time for this reason.
The useful mental split is network time (DNS, connect, TLS — mostly determined by distance and infrastructure) versus server time (the gap between the request being sent and the first byte coming back — mostly determined by your application and database).
Measuring TTFB with curl
curl exposes timing variables through its -w (write-out) option. Every value is measured in seconds from the start of the request, so they are cumulative, not per-phase:
curl -s -o /dev/null -w '
dns: %{time_namelookup}s
connect: %{time_connect}s
tls: %{time_appconnect}s
ttfb: %{time_starttransfer}s
total: %{time_total}s
status: %{http_code}
' https://example.com/
A typical result looks like this:
dns: 0.012s
connect: 0.041s
tls: 0.098s
ttfb: 0.612s
total: 0.655s
status: 200
Reading it phase by phase (subtract the previous line):
| Phase | Calculation | Result |
|---|---|---|
| DNS | time_namelookup | 12 ms |
| TCP connect | time_connect − time_namelookup | 29 ms |
| TLS handshake | time_appconnect − time_connect | 57 ms |
| Server wait | time_starttransfer − time_appconnect | 514 ms |
| Download | time_total − time_starttransfer | 43 ms |
In this example the network is fine; more than 80% of the TTFB is the server thinking. That tells you to look at the application, not the CDN or DNS.
A few practical notes:
time_appconnectis0for plainhttp://URLs because there is no TLS. Usetime_connectas the base in that case.- curl does not follow redirects unless you add
-L. Without it you measure only the first hop; with it, the timings cover the whole chain. Check%{num_redirects}to see how many hops there were. - Run the command several times. The first run may include a cold DNS cache or a cold application cache; the median of five or ten runs is more honest than any single one.
- Measure from somewhere other than the server itself. Running curl on the box removes network time and hides half the picture.
A quick loop for repeated samples:
for i in $(seq 1 10); do
curl -s -o /dev/null -w '%{time_starttransfer}\n' https://example.com/
done
What counts as a good TTFB
Google's web.dev guidance says sites should aim for a TTFB of 0.8 seconds or less for most real users, measured at the 75th percentile of page loads; values above 1.8 seconds are rated poor. Two caveats from the same guidance are worth repeating:
- TTFB is not one of the Core Web Vitals. It is a diagnostic metric that feeds into them, especially Largest Contentful Paint (LCP). A site with a 1-second TTFB has already spent 40% of the 2.5-second "good" LCP budget before the browser sees any HTML.
- The 0.8 s figure is for real users on real networks, including slow mobile connections. A synthetic check from a well-connected server should come in well under it; if your monitor already sees 800 ms from a data center, many of your visitors are seeing worse.
For a server-rendered page with no caching, a few hundred milliseconds of server wait is common. For a page served from a full-page cache or a CDN edge, server wait should be close to zero and TTFB should be dominated by network round trips.
Backend fixes that lower TTFB
Work from the biggest lever to the smallest. The curl breakdown tells you which group to start with.
1. Cache whole responses
The fastest code is code that does not run. If a page is the same for every anonymous visitor, serve it from a cache:
- nginx FastCGI cache or proxy cache for PHP or upstream apps.
- Varnish in front of the web server.
- A WordPress page-cache plugin that writes static HTML.
- A CDN that caches HTML, not only images and scripts.
A minimal nginx FastCGI cache looks like this (paths and zone names are placeholders):
# in the http {} block
fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2 keys_zone=PAGES:50m inactive=60m;
# in the server {} block
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($http_cookie ~* "session|logged_in") { set $skip_cache 1; }
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_cache PAGES;
fastcgi_cache_key $scheme$request_method$host$request_uri;
fastcgi_cache_valid 200 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status;
}
The X-Cache header lets you confirm with curl -I whether a request was a HIT or a MISS. Never cache responses for logged-in users or anything containing personal data.
2. Fix the database
For dynamic pages, the database is the most common source of server wait. Turn on the MySQL slow query log, find the queries that run on every page view, and check them with EXPLAIN. Missing indexes, N+1 query patterns (one query per item in a loop), and unbounded SELECT * on large tables are the usual suspects. An object cache such as Redis or Memcached for repeated lookups (settings, menus, counters) removes queries entirely.
3. Tune PHP-FPM and enable OPcache
Without OPcache, PHP recompiles every script on every request. Make sure it is on and large enough for your codebase:
; php.ini or conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
; in production, if your deploy script reloads PHP-FPM:
opcache.validate_timestamps=0
If you set validate_timestamps=0, code changes are invisible until PHP-FPM is reloaded, so add systemctl reload php8.2-fpm (adjust to your version) to your deploy.
Then check the pool. When all workers are busy, new requests queue and TTFB climbs even though each request is fast on its own. The FPM log says so explicitly with a warning like server reached pm.max_children setting. Size pm.max_children from available RAM divided by the average memory per worker, not by guesswork.
4. Cut network round trips
- Remove redirect chains: link and canonicalize directly to the final URL.
- Enable TLS 1.3 and HTTP/2 or HTTP/3 on the web server or CDN.
- Keep connections alive between your proxy and backend so each request does not pay for a new handshake.
- Put a CDN in front of the site if visitors are far from the origin. It shortens DNS, connect and TLS time even for uncached pages, because the handshake terminates at a nearby edge.
5. Make server time visible
Add a Server-Timing response header with your own measurements (for example Server-Timing: db;dur=120, app;dur=310). Browser developer tools display it in the Network panel's Timing tab, so you can see how the server wait splits between database and application code without a full APM product.
Why TTFB matters for users and monitoring
A slow first byte is felt on every page view, not just the first: each navigation repeats the wait. It also tends to get worse precisely when it matters, because the same bottlenecks that slow the server under load (exhausted workers, locked tables) are the ones that eventually cause timeouts. That makes TTFB a useful early-warning signal: a steady upward drift often appears days before a site starts returning errors. If your pages already feel sluggish, our guide on how website speed affects conversion rates covers the business side, and understanding DNS latency goes deeper into the first phase of the breakdown above.
FAQ
Is TTFB a Google ranking factor?
Google has not named TTFB as a ranking signal. It does use Core Web Vitals in its page experience signals, and a slow TTFB directly delays LCP, so improving TTFB usually helps the metrics Google has said it looks at.
Why is my TTFB fast in curl but slow in PageSpeed Insights?
PageSpeed Insights field data comes from real Chrome users at the 75th percentile, including slow mobile networks and distant locations. Your curl test probably runs from a fast connection near the server and may hit a warm cache.
Does a CDN always fix TTFB?
Only partly, unless it caches the HTML. A CDN shortens the network phases, but if every page request still goes to the origin, the server wait stays the same.
Key takeaways
- TTFB = DNS + connect + TLS + server processing + return trip, plus any redirects.
- Use
curl -wwithtime_namelookup,time_connect,time_appconnect,time_starttransferandtime_total, and subtract to get per-phase times. - Web.dev suggests 0.8 s or less at the 75th percentile of real users.
- Fix in order: full-page caching, database queries, PHP-FPM/OPcache, then network round trips.
- Track TTFB over time; a slow upward drift is an early warning of capacity trouble.
Uptime Monitor records this same breakdown — DNS, connect, TLS, time to first byte and total response time — on every check and, when a site slows down, names the phase that is the likely bottleneck, and you can set a response-time threshold that alerts you after several consecutive slow checks. The free Starter plan covers one website; you can create an account and have the first timing chart within minutes.