Server response time (TTFB): what is slow and how to fix it
TTFB mostly measures how long your server takes to build a page. Split it into network and server time with curl, then fix caching, queries and distance in that order.
Time to first byte (TTFB) is the time between sending a request and receiving the first byte of the response. For an HTML page it mostly measures how long your server takes to build the page: running the application, querying the database, rendering templates. Everything the browser does afterwards — downloading CSS, running scripts, painting — waits on it. This guide covers how DeskSEO measures it, how to read the number, and the fixes that move it most.
How DeskSEO measures TTFB
The SEO checker requests your homepage once over HTTPS and records the time to the first byte of the final response. The scoring treats:
| TTFB | Result |
|---|---|
| under 0.6 s | full marks |
| 0.6–1.5 s | minor issue: usable but slow |
| over 1.5 s | warning: slow enough to hurt users and crawling |
Two caveats are worth keeping in mind. First, it is a single measurement from a single location (the checker runs in Malaysia). A server in Frankfurt will look slower from there than from Paris simply because of distance; each round trip across continents adds tens to a couple of hundred milliseconds, and a new HTTPS connection needs several. Second, one request can hit a cold cache or a busy moment. A reading that swings between 0.4 s and 0.9 s across runs is normal variance; one that is consistently above 1.5 s is a real problem.
TTFB and Core Web Vitals
TTFB is not one of Google's Core Web Vitals, and Google does not publish a TTFB ranking threshold. It matters indirectly in two ways:
- Largest Contentful Paint (LCP) cannot happen before the HTML arrives, so a slow TTFB eats directly into the 2.5-second LCP target that Google uses for "good". A page with a 1.8 s TTFB has very little budget left.
- Crawling: Google adjusts how fast it crawls a site based on how quickly and reliably the server responds. Slow, error-prone responses mean fewer pages fetched per day, which matters most for large sites.
For field data from real users, the Core Web Vitals report in Search Console and the Chrome UX Report are the authority; a checker's single request is a quick diagnostic, not a replacement.
Measure it yourself with curl
curl can break a request into phases, which tells you where the time goes:
curl -s -o /dev/null -H "Accept-Encoding: gzip, br" \
-w "dns %{time_namelookup} connect %{time_connect} tls %{time_appconnect} ttfb %{time_starttransfer} total %{time_total} size %{size_download}\n" \
https://example.com/
All values are cumulative seconds from the start. Read them as differences:
connect − dns≈ network distance to the server (one round trip).tls − connect≈ the HTTPS handshake.ttfb − tls≈ server think time. This is the part your application controls.
If server think time is 50 ms but TTFB is 900 ms, the problem is distance or connection setup, not your code — a CDN or a closer host is the fix. If server think time alone is 800 ms, look at the application. Run the command five times and compare; also try a URL that should be cached and one that cannot be (for example a search results page).
Always send Accept-Encoding when measuring size: without it, curl receives the uncompressed HTML and the byte counts look several times larger than what browsers get.
The fixes, in the order that usually pays off
1. Full-page caching
Most pages are the same for every anonymous visitor. Building them on every request is the main reason CMS sites are slow. A page cache stores the finished HTML and serves it without running the application — typically turning hundreds of milliseconds of think time into single-digit milliseconds. Options include a caching plugin for your CMS, a reverse proxy cache (nginx fastcgi_cache, Varnish), or edge caching of HTML at a CDN. Make sure logged-in users, carts and personalised pages bypass the cache.
2. Opcode cache and runtime
For PHP, OPcache should be enabled in production; without it, every request recompiles every file. Running a supported, current version of your language runtime also tends to be measurably faster than an old one.
3. Slow database queries
If uncached pages are slow, profile them. Enable the database's slow query log, or a profiler in your framework, and look for: queries without suitable indexes on large tables, the same query repeated dozens of times in a loop (the N+1 pattern), and options or settings tables loaded in full on every request. One missing index can account for most of a page's think time.
4. Hosting location and the network
Host near your main audience, or put a CDN in front. Note that a CDN only helps TTFB for HTML if it caches HTML or keeps warm connections to your origin; a CDN that only caches images leaves HTML TTFB unchanged. HTTP/2 or HTTP/3 and TLS session resumption reduce connection overhead for repeat visits.
5. Shared hosting contention
On shared servers, a neighbour's traffic can slow your site. If TTFB varies wildly at different times of day while your code has not changed, compare against server load before blaming your application.
Compression and HTML weight
Two related checks sit next to TTFB in the DeskSEO scorecard:
- No gzip/Brotli for HTML: text compresses very well, typically to a fraction of its size. In nginx:
(gzip on; gzip_types text/html text/css application/javascript application/json image/svg+xml;text/htmlis always compressed once gzip is on; listing it is harmless.) Brotli needs a module but compresses text further. - Compressed HTML over 100 KB: this threshold is a DeskSEO heuristic, not a Google limit. Heavy HTML usually comes from large inline JSON (framework state hydration), inline SVG sprites, inline CSS of a whole design system, or hundreds of product cards rendered at once. Move reusable assets to cacheable files, paginate long lists, and send only the state the first view needs. Google does document a 15 MB limit on how much HTML it processes, so weight is about speed far more than about being read at all.
A practical sequence
- Run the checker twice a few minutes apart. Consistently over 1.5 s? Continue.
- Use the curl command from a machine near your audience to separate network time from server think time.
- If think time dominates, check that a page cache is active for anonymous visitors (a response header such as
x-cache: HITor your plugin's marker is a quick tell). - Profile an uncached page for slow queries.
- Confirm compression is on and look at what makes the HTML heavy.
- Re-measure, and compare with field data in Search Console's Core Web Vitals report over the following weeks.
Speed problems often return after a plugin update or a traffic change. Scheduled checks in a DeskSEO account record the TTFB of every run, so a gradual slowdown shows as a trend rather than a surprise; see How scoring works for the exact weights.
Questions
What is a good TTFB?
DeskSEO gives full marks under 0.6 seconds, flags 0.6–1.5 seconds as a minor issue and over 1.5 seconds as a warning. These are tool thresholds; Google publishes no TTFB ranking cut-off, but a slow TTFB eats into the 2.5-second LCP target.
Why is my TTFB different every time I check?
Caches may be cold, the server may be busy, and the network path varies. A single reading from one location is a diagnostic. Look for a consistent pattern across several runs and compare with field data in Search Console.
Will a CDN fix a slow TTFB?
Only if the CDN caches HTML or the slowness is mostly network distance. If your server needs a second to build each page and the CDN passes HTML through uncached, TTFB stays slow; add a page cache first.
Keep reading
Your SEO score dropped: a 15-minute triage checklist
Look at which criteria moved, deal with critical issues before anything else, tie the drop to a specific change, and confirm before acting…
What crawlers see on a JavaScript-rendered site
If your content is built in the browser, crawlers that do not run JavaScript see an empty page. Here is how to test it in two minutes and w…
HTTPS, www and redirect chains: getting one clean URL
Four versions of every domain exist by default. How to collapse them into one HTTPS URL with single-hop 301s, a complete certificate chain…