Slow TTFB: Fixing Time to First Byte
Time to First Byte is how long a visitor waits between asking for a page and receiving the first byte of its HTML.
It is not one of the three Core Web Vitals, but everything else waits for it: no image, stylesheet or text can load until the HTML starts arriving, so a slow TTFB drags Largest Contentful Paint down with it. web.dev offers roughly 0.8 seconds as a guide to a good TTFB. This guide shows how to split the wait into its parts, find out whether the server, the network or redirects are to blame, and fix each one.
What TTFB includes
TTFB is measured from the moment the browser starts navigating to the page until the first byte of the response arrives. That single number hides several separate waits, and each has a different fix.
| Part of the wait | What happens | Typical cause when slow |
|---|---|---|
| Redirects | The browser is sent to another address, sometimes more than once, before it reaches the real page. | http to https, bare domain to www, trailing slash rules, tracking links, old URLs. |
| DNS lookup | The browser finds the server's IP address. | Slow DNS provider, very short cache lifetimes on records. |
| Connection and TLS | The browser opens a connection and agrees encryption. | Server far from the visitor, no CDN, older TLS or HTTP settings. |
| Server processing | The server builds the HTML and starts sending it. | No page cache, slow database queries, heavy plugins or middleware, overloaded shared hosting, cold serverless functions. |
web.dev suggests about 0.8 seconds or less as a rough guide to a good TTFB, and treats more than 1.8 seconds as poor. Because TTFB is a diagnostic rather than a Core Web Vital, there is no pass or fail in Search Console for it. What matters is how much of your LCP budget of 2.5 seconds it uses up. If the HTML takes 1.5 seconds to arrive on a phone, only a second is left for the stylesheets, fonts and hero image.
How to measure it properly
Field data first
The Core Web Vitals checker shows field data from the Chrome UX Report, which includes TTFB alongside the three Core Web Vitals, and a lab test from one location. Real-user TTFB is often much worse than a test from a data centre, because visitors are on mobile networks, further away from your server and sometimes arriving through redirects from ads or emails.
From the command line
curl can split one request into its parts. Run it several times, since the first request after a quiet spell is often slower.
curl -o /dev/null -s -w "dns %{time_namelookup}\nconnect %{time_connect}\ntls %{time_appconnect}\nttfb %{time_starttransfer}\ntotal %{time_total}\nredirects %{num_redirects}\n" -L https://www.example.co.uk/
The figures are cumulative seconds from the start. Subtract tls from ttfb
to get the time the server spent building the page. If that gap is large, the server is the problem.
If connect and tls are large, distance or network set-up is.
In the browser
In the DevTools Network panel, click the HTML document and open Timing. "Waiting for server response" is the server processing part. For your own real-user data, the Navigation Timing API gives the same breakdown:
const [nav] = performance.getEntriesByType('navigation');
console.table({
redirect: Math.round(nav.redirectEnd - nav.redirectStart),
dns: Math.round(nav.domainLookupEnd - nav.domainLookupStart),
connect: Math.round(nav.connectEnd - nav.connectStart),
server: Math.round(nav.responseStart - nav.requestStart),
ttfb: Math.round(nav.responseStart)
});
Redirect time is only reported for same-origin redirects, so a redirect from another domain, such as an ad click tracker, shows as a gap rather than a number.
Ask the server where it spent the time
The Server-Timing response header lets your application report its own timings, which
DevTools shows in the Timing tab for the request.
Server-Timing: db;dur=412;desc="Database", tpl;dur=95;desc="Templates", cache;desc="MISS"
Even a rough version, timing the database and the template, makes it obvious which part of the server needs attention.
The fix, step by step
Step 1
Measure TTFB from where your visitors are
Run the page through the Core Web Vitals checker and note the field TTFB alongside LCP for mobile and desktop. Then test the same URL with curl from a machine in the country most of your visitors are in, five or six times, and note the lowest and highest figures. A large gap between runs suggests cache misses or an overloaded server. Test the exact URLs visitors land on, including any tracking parameters from ads.
Step 2
Break the wait into its parts
Use
the curl timings or the DevTools Timing tab to separate redirects, DNS, connection and TLS, and server
processing. Whichever part is largest decides where you work next. Add a Server-Timing header
to your application, even a rough one covering the database and template rendering, so that you can see
inside the server processing time. Check response headers for a cache status such as HIT
or MISS as well.
Step 3
Remove redirects before the real page
Follow the chain with curl -I -L and count the hops. Each one repeats the connection
and waiting time. Make every internal link, canonical tag, sitemap entry and advert destination point
at the final address, with the right protocol, host and trailing slash. Where a redirect must stay, handle
it in one hop at the web server or CDN rather than in the application, and send long-lived Strict-Transport-Security
so returning browsers skip the http step.
Step 4
Serve the HTML from a full-page cache
Most pages are the same for every logged-out visitor, so build them once and serve the stored
copy. Use your platform's page cache, a web server cache such as the nginx FastCGI cache, or cache HTML
at a CDN edge with s-maxage and stale-while-revalidate so visitors near any
edge location get a copy without waiting for your server. Purge the cache automatically when content
changes, so you can set longer lifetimes safely.
Step 5
Stop visitors missing the cache
Check
what makes a request skip the cache. Tracking parameters such as utm_source, gclid
and fbclid create a new cache key for every ad click unless you tell the cache to ignore
them. Cookies set for every visitor, often by analytics or consent tools, can make the cache bypass itself.
Set the cache to ignore marketing parameters and non-login cookies, and check the Vary header
is not splitting pages by cookie.
Step 6
Speed up the pages that cannot be cached
Basket, checkout, account and search pages must be built per visitor. Turn on the database slow query log and add indexes for the queries it lists, add an object cache such as Redis for repeated lookups, make sure PHP OPcache or your runtime's equivalent is on, and remove plugins or middleware that run on every request without need. If the hosting is shared and overloaded, a better plan or server may be the real fix.
Step 7
Recheck TTFB and LCP together
Repeat the curl tests and confirm both the lowest and the highest figures have come down, and that cached pages report a hit. Rerun the Core Web Vitals checker and compare LCP as well as TTFB, since the aim is a faster main content, not a better diagnostic. Field figures in the Chrome UX Report reflect the change gradually over 28 days as older visits drop out of the window.
Caching HTML without breaking the site
Full-page caching gives the biggest TTFB improvement for most sites, because serving a stored file takes a small fraction of the time needed to run the application and query the database. Done carelessly it also causes the most embarrassing bugs, such as one visitor seeing another's basket.
What can and cannot be cached
- Cache: home, category, product, article, service and landing pages for logged-out visitors.
- Do not cache: basket, checkout, account, order confirmation, admin, and any response that sets a session cookie.
- Handle separately: small personal parts of a cached page, such as a basket count or "Hello, Sam", can be loaded with a small request after the page arrives, so the page itself stays cacheable.
A CDN header for HTML
This tells shared caches such as a CDN to keep the page for ten minutes and serve a slightly stale copy for up to a minute while fetching a fresh one in the background, while browsers always revalidate:
Cache-Control: public, max-age=0, s-maxage=600, stale-while-revalidate=60
Support for these directives varies between CDNs, and some need HTML caching switched on as a separate setting, so check your provider's documentation.
An nginx page cache for a PHP site
fastcgi_cache_path /var/cache/nginx/pages levels=1:2 keys_zone=PAGES:50m inactive=60m;
map $http_cookie $skip_cache {
default 0;
~wordpress_logged_in 1;
~woocommerce_items_in_cart 1;
}
server {
location ~ \.php$ {
fastcgi_cache PAGES;
fastcgi_cache_key "$scheme$request_method$host$uri";
fastcgi_cache_valid 200 301 10m;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status;
# ... your usual fastcgi_pass and params
}
}
Using $uri instead of $request_uri in the key ignores the whole query string,
which stops tracking parameters from creating cache misses. Only do that if none of your cacheable pages
use query strings for real content, such as filters or pagination; otherwise strip only the marketing
parameters.
Platform notes
WordPress and WooCommerce
Uncached WordPress builds every page with PHP and many database queries, so a page cache usually makes the largest single difference. Many managed hosts include one; otherwise a caching plugin or a server-level cache does the job. Add a persistent object cache for logged-in and WooCommerce traffic, and review plugins that run database queries on every request. The WordPress Core Web Vitals guide covers hosting and caching choices.
Shopify and other hosted platforms
You cannot change the server, and pages are served through the platform's own CDN. Slow TTFB here usually comes from redirects, from very complex theme templates with many loops over large collections, or from visitors arriving through tracking links. Simplify heavy sections and make sure links point at final URLs.
Next.js, Nuxt and other frameworks
Pages rendered on the server for every request are only as fast as their data fetching. Where the content is the same for everyone, pre-render it at build time or use incremental regeneration so the HTML is served from a cache, and stream the page so the first bytes are sent before slow data arrives. Serverless functions that have not run for a while can be slow on the first request; see the Next.js and Nuxt guide.
Static sites
Static HTML served from a CDN is usually fast already. If TTFB is still slow, check for redirects, a CDN that is not caching HTML, or a CDN that sends every request back to a distant origin server.
Edge cases
- Fast for you, slow for visitors. You probably have a warm cache and live near the server. Test from a clean browser, from another network and from the country your visitors are in.
- Slow only on the first visit after a quiet period. Caches expire and serverless
functions go cold. Longer cache lifetimes with purge on update, and
stale-while-revalidate, keep a copy ready. - Slow only from ads. Paid clicks carry tracking parameters and sometimes go through the ad platform's redirects. Check the cache ignores the parameters and the destination URL is final.
- Slow for UK visitors on a host abroad. Distance adds to every connection. A CDN in front of the site, or hosting nearer your audience, helps more than any code change.
- 103 Early Hints. Some CDNs can send an early response listing key files to preload while the server is still building the page. It does not reduce TTFB itself, but it lets the browser start on stylesheets and fonts sooner, which can help LCP.
Keeping TTFB low
- Watch the cache hit rate. A falling hit rate after a new plugin, cookie or campaign is an early warning.
- Keep links final. Update internal links, canonicals and sitemaps whenever URLs change, so redirects stay rare.
- Log slow queries continuously and review them after each release.
- Test TTFB after hosting or DNS changes, and after adding anything that runs on every request.
- Keep Server-Timing headers in production so you can see server time for any request in DevTools.
For the other parts of page speed, see redirect chains and the page speed fixes.
1. Is TTFB a Core Web Vital?
No. The three Core Web Vitals are Largest Contentful Paint, Interaction to Next Paint and Cumulative Layout Shift. TTFB is a diagnostic measurement that helps explain LCP and First Contentful Paint. It still matters a great deal, because nothing on the page can load until the HTML starts arriving, so a slow TTFB uses up much of the 2.5 second LCP budget before the browser can even request the hero image, stylesheets or fonts.
2. What is a good TTFB?
web.dev offers roughly 0.8 seconds or less as a guide to a good TTFB for real visitors, and treats more than 1.8 seconds as poor. It describes this as a rough guide rather than a hard threshold, because what really matters is whether your LCP is good. A page with a TTFB of one second can still pass LCP if everything else is efficient, while a page with fast TTFB can fail if its hero image is heavy or discovered late.
3. Why is my TTFB slow only on mobile?
Mobile connections take longer to set up, especially on 4G with weak signal, so the DNS, connection and TLS parts of TTFB are larger. Mobile visitors are also more likely to arrive from social media or ads through redirects and tracking links, which add hops and can cause cache misses. Compare the connection timings with the server timings to see which part grows on mobile, and check the landing URLs those visitors actually use.
4. Will a CDN fix slow TTFB?
A CDN always helps with connection time by answering from a location near the visitor. It only fixes slow server processing if it caches your HTML, and many CDNs cache images, scripts and stylesheets by default but not HTML. Check the response headers of your page for a cache status. If every request says miss or bypass, the CDN is passing every page back to your server and TTFB will barely change until HTML caching is switched on and configured.
5. Do redirects count towards TTFB?
Yes. TTFB is measured from the start of the navigation, so every redirect before the final page adds its own connection and waiting time. A chain of http to https, then to www, then to a trailing slash version can add hundreds of milliseconds on a mobile connection. Point links, adverts, canonical tags and sitemaps at the final address, and combine unavoidable redirects into a single hop handled at the web server or CDN.
6. Why does my caching plugin not seem to help?
The most common reasons are that visitors are missing the cache, not that the cache is slow. Tracking parameters on ad and email links create new cache keys, cookies set by analytics or consent tools can make the cache bypass itself, and very short cache lifetimes mean pages expire before the next visitor arrives. Check the cache status header on real landing URLs, and make sure the plugin is not being overridden by a server or host setting.
7. Can TTFB be too low to matter?
Once TTFB is comfortably fast, further server work gives little benefit, and the effort is better spent on the page itself. If your TTFB is a few hundred milliseconds and LCP is still slow, look at render-blocking resources, the size and priority of the hero image, and fonts. Keep measuring TTFB to catch regressions, but do not chase tiny gains at the expense of fixes that would make the main content appear sooner.
The Core Web Vitals checker shows real-user field data and a Lighthouse lab test side by side for your URL, breaks down each metric and points to the assets and code behind every failure, so you can see whether a slow server response is holding LCP back.
Open Core Web Vitals Fixes โ