Why Is My TTFB So High: How to Fix Time to First Byte
Time to First Byte measures how long the browser waits, after a visitor asks for a page, before the first byte of the HTML arrives. Nothing can appear on screen until that byte lands, so a slow TTFB pushes back every other loading milestone, including Largest Contentful Paint. A high figure usually comes from one of a few places: redirects before the real page, a server that builds every page from scratch, slow database work or plugins, distance between the server and the visitor, or hosting that cannot keep up. This guide shows you how to measure TTFB properly, how to split it into stages so you know which one is slow, and the server, caching and CDN changes that bring it down for real visitors.
What TTFB measures and what counts as high
TTFB runs from the moment navigation starts to the moment the first byte of the response arrives. For a real visitor that includes more than your server's thinking time. It covers any redirects, the DNS lookup, the TCP connection, the TLS handshake, the time the request spends travelling, the time your server takes to build the page and the time the first byte takes to travel back.
| TTFB at the 75th percentile | Rough rating |
|---|---|
| 0.8 s or less | Good |
| Over 0.8 s up to 1.8 s | Needs improvement |
| Over 1.8 s | Poor |
TTFB is not one of the three Core Web Vitals, and Google describes these thresholds as a rough guide rather than a pass mark. It matters because it comes first. If the HTML takes 1.5 seconds to arrive, your LCP cannot be better than 1.5 seconds plus the time to load and draw the main element, which makes the 2.5 second LCP target very hard to meet. The LCP guide shows how TTFB fits into the four parts of LCP time.
In Lighthouse the related audit is called Reduce initial server response time, or document request latency in newer versions. It is measured from Google's test servers, so it shows the server's speed from one place at one moment. The TTFB in the field data section of PageSpeed Insights comes from real Chrome users and includes their redirects and network conditions.
Common causes of a high TTFB
| Cause | Typical sign | Usual fix |
|---|---|---|
| Redirect chain | Field TTFB much higher than a direct curl test | Link to the final URL; one redirect at most |
| No page cache | Every request takes the same long time | Full-page caching for anonymous visitors |
| Slow queries or plugins | High server time even on a quiet server | Profile, replace or fix the slow code |
| Calls to outside APIs during the request | TTFB varies wildly between runs | Cache API results or load them later |
| Too few PHP or Node workers | Fine when quiet, slow at busy times | Raise workers, or upgrade the plan |
| Server far from visitors | Connect and TLS times are high | CDN that caches HTML, or a nearer server |
| Sleeping or cold servers | First request after a pause is very slow | Keep-warm settings or an always-on plan |
| Slow DNS | Long DNS lookup on first visits | A fast DNS provider |
Redirects are easy to miss because your own tests usually request the final URL. A visitor who types the bare domain or follows an old link may go from http:// to https://, then to www, then to a trailing slash version. Each step is a full round trip before your server even starts on the real page. The redirect chain guide explains how to collapse these.
How to measure TTFB and split it into stages
Use curl from a terminal, or from your server over SSH to remove network distance from the picture. This command prints each stage in seconds, measured from the start of the request:
curl -o /dev/null -s -w "dns %{time_namelookup} connect %{time_connect} tls %{time_appconnect} ttfb %{time_starttransfer} total %{time_total}\n" https://example.com/
Each figure is cumulative. Read the gaps between them:
- dns high: DNS lookup is slow. Rare once the record is cached, but it affects first visits.
- connect minus dns high: the server is far away or the network is congested.
- tls minus connect high: distance again, or an old TLS set-up without session reuse.
- ttfb minus tls high: your server is slow to build the page. This is the most common finding.
Run it five times in a row. The first result may include a cold cache; the later ones show the steady state. To compare cached and uncached responses, add a random query string, which most page caches treat as a new page:
for i in 1 2 3 4 5; do
curl -o /dev/null -s -w "%{time_starttransfer}\n" "https://example.com/?nocache=$RANDOM"
done
Add -L to follow redirects and see the total cost of a chain, and -I on its own to read headers such as x-cache, cf-cache-status or age, which tell you whether a cache answered.
In the browser
In Chrome DevTools, open the Network panel, reload, click the document request and open the Timing tab. Waiting for server response is the server time. The rows above it show the connection stages, and redirects appear as separate requests at the top of the list.
Ask the server where the time goes
A Server-Timing header lets your code report its own stages, and DevTools shows them in the Timing tab. For example, a page that reports database and template time:
Server-Timing: db;dur=420, tpl;dur=95, cache;desc="MISS"
The changes that cut TTFB most
Full-page caching
For pages that look the same to every visitor who is not logged in, the biggest win by far is serving a stored copy of the finished HTML instead of building it again. A cached page is typically returned in well under 100 ms from the server. On nginx you can cache responses from PHP-FPM directly:
# in the http block
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=PAGES:50m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
map $http_cookie $skip_cache { default 0; ~wordpress_logged_in 1; }
location ~ \.php$ {
fastcgi_cache PAGES;
fastcgi_cache_valid 200 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status;
# existing fastcgi_pass and params here
}
Never cache pages that differ per visitor, such as baskets, checkouts and account pages, and make sure the cache bypasses logged-in users.
Find slow code
When pages must be built per request, profile them. On WordPress, the Query Monitor plugin lists every database query with its time and the plugin that ran it. Look for pages with hundreds of queries, single queries over 50 ms and HTTP API calls made during the page build. Turn on the MySQL slow query log to catch problems across the whole site:
sudo mysql -e "SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 0.5;"
sudo tail -f /var/lib/mysql/$(hostname)-slow.log
A persistent object cache such as Redis also helps database-heavy sites by keeping the results of repeated queries in memory between requests.
Move HTML closer to visitors
A CDN only helps TTFB if it caches the HTML itself. Putting images and CSS on a CDN is good for those files, but the page request still travels to your server. If your audience is spread across countries, cache HTML at the edge, for example with a Cloudflare cache rule set to cache eligible HTML for anonymous visitors, or with Cloudflare APO on WordPress. The CDN and origin redirects guide covers loops that can appear when you change CDN settings.
Platform notes
- WordPress: use one page caching plugin, or the cache your host provides, not both. Check PHP is a current supported version. Remove plugins that run heavy queries on every page, and check WP-Cron is not running long tasks during visitor requests; on busy sites, trigger it from a real server cron job instead. See WordPress page speed.
- Plesk: under the domain's PHP settings, the PHP-FPM
pm.max_childrenvalue limits how many requests run at once. If it is too low, requests queue and TTFB climbs at busy times. Check the domain's error log for messages saying the limit was reached. Plesk also offers nginx caching per domain. - Shopify: the server and CDN are managed for you, so TTFB problems usually come from heavy Liquid in the theme, such as large loops over collections or many apps injecting code. Test with a fresh copy of the default theme to compare.
- Next.js: pages rendered on every request are slower than static or incrementally regenerated pages. Use static generation where content allows, and cache data fetches. See Next.js page speed.
A worked example
A WordPress shop for a small UK business has a field TTFB of 1.9 seconds on mobile. A curl test with a random query string gives about 1.7 seconds each time, while the same page without the query string returns in 0.15 seconds with X-Cache: HIT. The server is fast when the cache answers, so the slow part is building the page.
Query Monitor shows 400 database queries on product category pages, almost all from a related-products plugin running a separate query for each item. Replacing it with the theme's built-in related products cuts the count to 60 queries and the uncached build time to 0.45 seconds. The team also finds that every visitor arriving from an old campaign link goes through two redirects, and updates the links. Four weeks later the field TTFB sits under 0.6 seconds, and mobile LCP has improved by a similar amount.
The fix, step by step
Step 1
Measure TTFB in stages
Run the curl stage command with time_namelookup, time_connect, time_appconnect and time_starttransfer against the page five times. Run it from your own computer and again from the server itself over SSH. Note the gap between the TLS and TTFB figures, which is the server build time. If the gap is small but the connect time is large, distance is the issue. Record the numbers so you can confirm later that each change made a measurable difference.
Step 2
Compare cached and uncached
Request the same page with a random query string such as ?nocache=123 and compare the TTFB with a normal request. Read the response headers with curl -I and look for a cache status header such as X-Cache or cf-cache-status. If the cached version is fast and the uncached one is slow, caching will give the biggest win. If both are slow, the cache is not answering at all, or the slowness comes before the server, such as DNS or distance.
Step 3
Remove redirect hops
Run curl -sIL on the bare domain with http:// and follow each Location header to the final URL. Every hop adds a full round trip before the page begins. Make sure any old address goes to the final URL in a single redirect, and update internal links, menus, canonical tags and campaign links to point at the final URL directly. Confirm by running the command again and checking that it shows one redirect at most before a 200 response.
Step 4
Turn on full-page caching
Enable full-page caching for anonymous visitors, using your host's cache, a caching plugin or nginx fastcgi_cache. Exclude baskets, checkouts, account pages and logged-in users. Use only one page cache layer to avoid conflicts. Set the cache to ignore marketing parameters such as utm_source. Confirm it works by requesting a page twice in a private window and checking that the second response shows a cache hit and a TTFB well under 200 ms from the server.
Step 5
Profile slow pages
For pages that cannot be cached, find where the build time goes. On WordPress, install Query Monitor and open a slow page while logged in as an admin, then sort queries by time and group them by component. Elsewhere, add a Server-Timing header or enable the slow query log. Look for plugins running hundreds of queries, missing database indexes and outside API calls during the request. Fix or replace the worst offender, then measure the uncached TTFB again.
Step 6
Check workers and hosting limits
Look at your server's error log for messages that PHP-FPM reached pm.max_children, and at CPU and memory use during busy periods. If requests queue, raise the worker limit within the memory you have, or move to a plan with more resources. Check whether your hosting puts idle sites to sleep, which makes the first request after a pause very slow. Confirm by testing TTFB at your busiest time of day and comparing it with a quiet period.
Step 7
Cache HTML near visitors
If your visitors are spread across countries, set up a CDN rule that caches HTML for anonymous visitors, not only images and CSS. On Cloudflare, use a cache rule that makes eligible pages cacheable, or APO for WordPress, and bypass the cache when login or basket cookies are present. Test from a distant location and read the cf-cache-status header. A value of HIT with a low TTFB confirms the edge is answering for you.
Step 8
Check field data after the change
Lab tests confirm the fix works, but real-visitor TTFB includes redirects, distance and busy periods. Watch the TTFB and LCP figures in the field data section of PageSpeed Insights, which reflects the previous 28 days, and the Core Web Vitals report in Google Search Console. Expect the figures to move gradually over several weeks. If field TTFB stays high while your tests are fast, look again at redirects from external links and at visitors far from your server.
Edge cases worth knowing
- Cache warm-up. After a cache is cleared, the first visitor to each page waits for a full build. Pages that get little traffic may almost always be uncached; a preload option in your caching tool helps.
- Cookies and query strings. Tracking parameters such as
utm_sourceand cookies set by plugins can make the cache skip a page. Configure the cache to ignore marketing parameters. - Logged-in users. Admins and members usually bypass the cache, so their TTFB is always higher. Do not judge site speed from your own logged-in browser.
- Early hints and streaming. Some platforms send a
103 Early Hintsresponse or stream the start of the HTML early. These can lower measured TTFB without changing total build time, so look at LCP as well. - Lab versus field. One test location can be fast while visitors on another continent see slow times. Compare field data with tests from several locations.
Keeping TTFB low
Add a simple check to your routine: run the curl stage command on your home page and one page from each template every month, cached and uncached, and keep the numbers in a spreadsheet. A sudden rise usually follows a new plugin, a theme update or a hosting change, and it is far easier to trace within a week than within a year.
Watch the TTFB figure in the field data in PageSpeed Insights and the Core Web Vitals report in Search Console, because real-visitor numbers include redirects and distance that lab tests miss. Re-test after every caching or CDN change, and read the cache status header to confirm the cache is still answering.
1. What is a good TTFB?
A good TTFB is about 0.8 seconds or less for most real visitors, measured at the 75th percentile. Google gives this as a rough guide, with values over 1.8 seconds considered poor. TTFB is not a Core Web Vital itself, but it is the first part of every page load, so a high value makes it hard for Largest Contentful Paint to reach the 2.5 second target. For cached pages served from a nearby server, well under 0.3 seconds is achievable.
2. Why is my TTFB high when my site is fast for me?
Your TTFB is probably high for visitors because they see a different path through your site than you do. You may be hitting a warm cache, testing the final URL directly or sitting close to the server. Visitors may arrive through redirects, land on uncached pages, come from far away or visit when the server is busy. Being logged in can make your own experience slower instead. Test in a private window, from several locations and with a random query string.
3. Does a CDN fix high TTFB?
A CDN fixes high TTFB only when it caches the HTML of your pages, not just images, scripts and stylesheets. If the CDN passes every page request to your server, visitors still wait for your server to build the page, and the CDN can even add a little time. Set up rules to cache HTML for anonymous visitors, and confirm with the cache status header. A CDN also shortens connection and TLS time for distant visitors, which helps a little on its own.
4. How do I check TTFB with curl?
You can check TTFB with curl by running it with the write-out option and the time_starttransfer variable, which reports the seconds until the first byte arrived. Adding time_namelookup, time_connect and time_appconnect shows the DNS, connection and TLS stages as well, so you can see where the wait comes from. Send the output to /dev/null so only the timings print. Run it several times, because the first request may hit a cold cache and give a misleading result.
5. Do redirects affect TTFB?
Yes, redirects add directly to the TTFB that real visitors experience. Each redirect is a full request and response before the browser can ask for the next address, and a change of host, such as from the bare domain to www, can also need a new connection. Field TTFB in PageSpeed Insights includes this time. A chain from http to https to www can add several hundred milliseconds on mobile, so link to the final URL and keep any redirect to a single hop.
6. Why is TTFB high on WordPress?
WordPress TTFB is usually high because pages are built from scratch on every request without a page cache, or because plugins run many slow database queries during each build. Other common causes are an old PHP version, too few PHP workers on busy hosting, scheduled WP-Cron tasks running during visitor requests and plugins that call outside services while the page is built. Full-page caching fixes most of it for visitors, and Query Monitor shows which plugin is behind the slow uncached pages.
7. Will lowering TTFB improve my LCP?
Lowering TTFB almost always improves LCP by a similar amount, because the browser cannot start drawing anything until the HTML arrives. A saving of 500 ms on TTFB usually brings LCP 500 ms earlier, unless something else, such as a lazy-loaded hero image or blocking scripts, was holding LCP back by more. Check the LCP breakdown in PageSpeed Insights after the fix. If TTFB is now small and LCP is still slow, look at how the main element is loaded and drawn.
The PageSpeed checker on aiwebpageseo.com reports server response time alongside LCP and the files that slow your page, so you can see whether TTFB is the first thing to fix.
Open Core Web Vitals Fixes โ