Slow Site? Diagnosing and Fixing TTFB Step by Step
Where time-to-first-byte goes — DNS, TLS, server, database — and how to diagnose and cut each stage.
Half of every “my site is slow” complaint is decided before the browser receives a single byte. TTFB (Time To First Byte) is the time from the moment a user requests your page until the first byte of the response arrives. If that number is bad, no amount of image optimization will make the site feel fast. Google’s rule of thumb: under 200ms is great, over 800ms is poor — and a TTFB above one second almost always has a fixable cause.
The key insight is that TTFB is not one number but a sum of stages: DNS lookup → TCP connection → TLS handshake → server think-time → first byte on the wire. Installing a cache plugin before knowing which stage is slow is like taking medicine without a diagnosis. This guide walks through measuring each stage, identifying the culprit per stage, and applying fixes in order of impact. To see your own breakdown right now, drop a URL into the response time tool — it splits DNS, connect, TLS and wait time for you.
What TTFB includes — and what it does not
Get the scope right first, so you don’t fix the wrong layer. TTFB is the sum of five stages:
- DNS lookup — resolving the domain name to an IP address.
- TCP connection — the 3-way handshake with the server (one round trip, proportional to RTT).
- TLS handshake — negotiating HTTPS encryption (typically one or two extra round trips).
- Server think-time — everything the server does to build the HTML: database queries, template rendering, external API calls. This is usually the biggest slice.
- First byte in transit — the first chunk of the response crossing the network.
What TTFB does not include: the rest of the HTML download, images, JavaScript, CSS, rendering, and script execution. So if your TTFB is 180ms but the page takes five seconds, the server is innocent — look at the front end instead. Check resource weight with the page weight analyzer and verify gzip/brotli with the compression checker before touching the backend.
Measure each stage — one curl command is enough
If you have a terminal, curl’s -w (write-out) option times every stage in a single run:
curl -o /dev/null -s -w "DNS: %{time_namelookup}s
TCP connect: %{time_connect}s
TLS done: %{time_appconnect}s
First byte: %{time_starttransfer}s
Total: %{time_total}s
" https://example.com/Each value is cumulative, so subtract the previous one to get per-stage durations:
- DNS stage =
time_namelookup - TCP stage =
time_connect−time_namelookup - TLS stage =
time_appconnect−time_connect - Server think-time (the big one) =
time_starttransfer−time_appconnect
No terminal handy? The response time toolshows the same breakdown in your browser. Always measure 3–5 times and take the median — the first request hits cold DNS and server caches and is reliably the slowest.
Finding the culprit, stage by stage
| Slow stage | Usual suspects | Typical fix |
|---|---|---|
| DNS (> 100ms) | Slow nameservers, TTL too short to cache, long CNAME chains | Move to an anycast DNS provider (Cloudflare, Route 53), flatten CNAME chains |
| TCP (> 100ms) | Server physically far from users — every round trip is expensive | Add a CDN, or move the origin to a region closer to your audience |
| TLS (> 150ms) | No session resumption, full TLS 1.2 handshakes, distant terminator | Enable TLS 1.3, session tickets, OCSP stapling; terminate TLS at a CDN edge |
| Server think-time (> 300ms) | No page cache, unindexed database queries, serverless cold starts, underpowered shared hosting | Full-page cache, query optimization and indexes, provisioned/warm instances, better hosting |
| First byte in transit | Server buffers the whole response before sending anything | Stream or flush early; compress the response to shrink transfer |
In practice, server think-time is the number-one cause on any site with TTFB above 800ms. DNS, TCP and TLS combined rarely exceed 200–300ms; everything beyond that is your server building the HTML.
Fixes, ordered by impact
- Full-page cache (biggest win).Instead of hitting the database and rendering templates on every request, store the finished HTML and serve it directly. On WordPress that’s a caching plugin; on a custom app it’s a reverse-proxy cache (nginx, Varnish) or static generation (SSG). Think-time drops from hundreds of milliseconds to single digits.
- Database indexes and slow-query cleanup. Cache misses and logged-in users still hit the origin. Turn on the slow-query log and add indexes to anything over 100ms.
- Keep-alive plus HTTP/2 and HTTP/3. Reusing connections removes TCP and TLS costs from every request after the first; HTTP/3 (QUIC) cuts handshake round trips outright.
- CDN. Code cannot shorten physical distance. If your users span continents, let an edge node terminate TLS nearby and, ideally, serve cached HTML — overseas TTFB improves dramatically.
- Better hosting. If think-time is still high after all of the above, the CPU is slow or your shared host is oversubscribed. At that point the fix is a server change, not a code change.
Worked example: a 1.4s WordPress TTFB cut to 180ms
Here is a real diagnostic run on a WordPress blog whose TTFB measured 1.4 seconds.
| Stage | Before | After |
|---|---|---|
| DNS | 35ms | 35ms |
| TCP connect | 40ms | 40ms |
| TLS handshake | 85ms | 60ms (TLS 1.3) |
| Server think-time | 1,240ms | 45ms |
| Total (TTFB) | ~1.4s | ~180ms |
The network stages added up to just 160ms — nothing wrong there. The remaining 1.24 seconds was pure server think-time: PHP rendering the theme and running dozens of database queries on every single request. Installing one full-page cache plugin (WP Super Cache style), which serves finished HTML straight from disk, brought think-time down to 45ms; enabling TLS 1.3 shaved the handshake as well. TTFB went from 1.4s to 180ms with zero code changes. The lesson: because the diagnosis came first, no time was wasted on image optimization or other fixes that would not have touched the real problem.
Once TTFB is healthy, audit what happens after the first byte: run the compression checker to confirm brotli/gzip on your HTML, CSS and JS, and the page weight analyzer to make sure total transfer size is reasonable — that is where the rest of perceived speed lives.
Frequently asked questions
What is a good TTFB?
My TTFB is fast but the site still feels slow. Why?
Will a cache plugin always fix a slow TTFB?
How much does a CDN reduce TTFB?
Why does my TTFB vary so much between measurements?
Tools to use with this guide
Related guides
- Cache-Control Done Right: Making Browser Caching WorkWhat max-age, no-cache, no-store and immutable really mean, with recommended policies per resource type.
- Enabling gzip and Brotli: Cut Transfer Size by 70%What compressing text resources buys you, nginx/Apache config, verification and common mistakes.
- Core Web Vitals (LCP, CLS, INP) Improvement ChecklistWhat LCP, CLS and INP measure, and a practical checklist to improve each score.