Your page load time is made up of many steps. But one metric sits right at the start of the chain and affects everything that comes after it: Time to First Byte, or TTFB.
If your TTFB is high, it doesn't matter how well you've optimized your images or minified your JavaScript. The browser is sitting there waiting before it can do anything else. Fixing it often delivers more visible improvement than any front-end optimization you can make.
Here's what causes a slow TTFB, how to measure it accurately, and exactly what to do about it.
What TTFB Actually Measures
TTFB is the time between the browser sending an HTTP request and receiving the first byte of the response. It captures three things happening in sequence:
- DNS lookup — resolving your domain to an IP address
- TCP connection + TLS handshake — establishing a secure connection to the server
- Server processing time — the server generating and beginning to send the response
Google's Core Web Vitals guidelines classify TTFB as good below 800ms, needing improvement between 800ms–1800ms, and poor above 1800ms. But realistically, you should be targeting under 200ms for most pages, and under 500ms even for dynamic, database-driven content.
How to Measure TTFB Correctly
Measuring TTFB once and calling it done will mislead you. The first request after a server restart is cold — caches are empty, connections haven't been established. You need to distinguish between cold and warm TTFB.
Browser DevTools
Open Chrome DevTools, go to the Network tab, reload your page, and click on the main document request. Under the Timing tab, you'll see TTFB labeled as "Waiting (TTFB)". Run this several times and note both the first load and subsequent loads.
WebPageTest
WebPageTest is the most detailed free tool available. Run a test from a location close to your actual users. Look at the waterfall chart — the teal bar for your HTML document shows TTFB clearly. Run at least 3 tests and use the median result.
curl for Raw Timing
For precise, repeatable measurement without browser overhead, use curl:
curl -o /dev/null -s -w \ "DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \ https://yourdomain.comThis breaks down every phase. If time_namelookup is high (over 100ms), your DNS is the bottleneck. If time_appconnect minus time_connect is high, your TLS configuration needs work. If those are fine and TTFB is still slow, the problem is server-side.
The Most Common Causes of High TTFB
1. No Page Caching
This is the most impactful fix for most sites. When every visitor triggers a full PHP execution cycle — bootstrapping WordPress, querying the database, assembling HTML — you're doing expensive work that could have been done once and stored.
A cached page is served in under 10ms. An uncached WordPress page on a reasonable server typically takes 200–800ms. On a busy or underpowered server, it can exceed 2 seconds.
For WordPress, plugins like W3 Total Cache, WP Super Cache, or LiteSpeed Cache work well. Server-level caching (handled by Nginx FastCGI cache or Varnish) is even faster because it bypasses PHP entirely.
2. Slow Database Queries
Even with caching, some pages are dynamic and can't be cached — user dashboards, checkout pages, search results. Here, database performance dominates your TTFB.
Enable the MySQL slow query log to find culprits:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0.5; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';Then use EXPLAIN on slow queries to check if indexes are being used. Missing indexes on columns used in WHERE, JOIN, or ORDER BY clauses are a very common cause of queries that take seconds when they should take milliseconds.
3. PHP Configuration and Opcache
PHP has to parse and compile your application's code on every request — unless you have an opcode cache. OPcache stores the compiled bytecode in memory so PHP skips the parsing step entirely.
Check if OPcache is active:
php -r "echo opcache_get_status()['opcache_enabled'] ? 'Enabled' : 'Disabled';"If it's enabled, make sure the memory allocation is sufficient. A common default of 128MB runs out quickly on large applications:
; php.ini opcache.memory_consumption=256 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 ; Disable in production for max speed4. Geographic Distance to the Server
Physics doesn't negotiate. A request from Sydney to a server in Frankfurt travels roughly 17,000 km. Even at the speed of light, that round-trip adds 100–150ms before your server does anything. Add in routing hops and that number grows.
If your audience is international, a CDN solves this by serving cached responses from edge nodes close to your users. Cloudflare, BunnyCDN, and Fastly all work well for this purpose. For truly global dynamic applications, you'll want to consider a server location closer to your primary audience, or an architecture that replicates data to multiple regions.
5. Shared Hosting Resource Contention
On shared hosting, your server's CPU and memory are split with dozens or hundreds of other sites. A traffic spike on a neighbor's site can make your TTFB jump from 200ms to 3 seconds with no change on your end. This is one of the clearest signals that it's time to move to a VPS or managed dedicated environment where your resources are actually yours.
Quick Wins to Implement Today
- Enable HTTP/2 or HTTP/3. Both dramatically reduce connection overhead through multiplexing. Check with curl -I --http2 https://yourdomain.com — look for HTTP/2 200 in the response.
- Use a fast DNS provider. Move from your registrar's default DNS to Cloudflare (1.1.1.1) or Amazon Route 53. DNS lookup time drops from 50–200ms to under 10ms.
- Enable Keep-Alive. Persistent connections reuse the TCP handshake for subsequent requests. In Nginx: keepalive_timeout 65;
- Compress your responses. Enable Gzip or Brotli at the server level. Brotli compresses HTML, CSS, and JS around 15–20% better than Gzip.
- Tune your PHP-FPM pool. If pm.max_children is too low, requests queue up. Monitor pm.status and increase the pool size if you're consistently hitting the limit.
How to Know When You've Fixed It
Run a full baseline before you start changing anything. Record TTFB for at least 5 URLs — your homepage, a category page, a product or post page, and any dynamic pages. Measure from multiple locations using WebPageTest.
After each change, measure again. TTFB improvements compound quickly: fixing OPcache might save 80ms, adding page caching saves another 400ms, and moving to a faster DNS saves 60ms. Suddenly you're down from 700ms to under 200ms without touching a single line of application code.
On a well-configured managed server, TTFB for cached pages should sit comfortably under 50ms. For uncached dynamic pages, under 300ms is achievable for most WordPress or PHP applications. If you're significantly above those numbers after working through this list, the bottleneck is almost certainly at the infrastructure level — server resources, location, or configuration — rather than your application.