
Time to First Byte is the time between your browser sending a request and the first byte of the response arriving back — not the time until the page finishes loading. It bundles DNS lookup, TCP connection, TLS handshake, and however long your server takes to generate the response, but it stops the moment the first byte of the body shows up. Everything after that — downloading CSS, images, and JS, then rendering — is a separate measurement, and optimizing it will not touch your TTFB at all.
This distinction matters because a lot of "speed optimization" advice — minifying JS, compressing images, lazy-loading below-the-fold content — has zero effect on TTFB. If a page shows a 900ms TTFB in your monitoring tool, the fix lives in server processing time, database query time, or network path, not in your front-end bundle. A page with a heavy front end but a fast backend can still post a great TTFB; a lean, minimal page sitting behind a slow, unoptimized PHP-FPM pool will still post a bad one. Treat TTFB as a server and network health metric, and treat everything downstream of it as a separate, unrelated problem.
Before changing any config, measure where the time actually goes. curl's -w flag can break a single request into DNS lookup, connect, TLS negotiation, and time-to-first-byte, so you fix the real bottleneck instead of guessing.
Run this five or six times and look at where the gaps show up. If time_namelookup is a large slice of the total, your DNS resolver or the domain's DNS provider is the bottleneck, not your server. If time_starttransfer minus time_appconnect is the biggest gap, that is pure server processing time — the request reached your server and it took a while to hand back a response, which points squarely at PHP-FPM, database queries, or missing caching. Run the same command from a second location, ideally a VPS in a different region, to separate "my server is slow" from "my server is far away from this particular tester."
If time_starttransfer is where your time is going, OPcache and your PHP-FPM process manager are the first two things to check. Without OPcache, PHP recompiles every script on every single request instead of reusing compiled bytecode from memory — on a WordPress site loading dozens of plugin files, that recompilation alone can add 100-300ms to every page.
Next, check your PHP-FPM pool's process manager. pm=ondemand spawns a fresh child process the moment a request arrives and there is no idle worker waiting, which adds a real, measurable startup delay to TTFB on low-traffic sites and after any period of inactivity. Switching to pm=dynamic or pm=static keeps a pool of warm workers ready, trading a bit of idle RAM for consistently low TTFB — worthwhile on almost any production site that gets more than occasional traffic.
pm=static with pm.max_children set too high is the single most common cause of an out-of-memory crash on a small VPS. Each child process holds its own memory, so multiply pm.max_children by your average PHP process size and confirm the total stays comfortably under available RAM before switching from ondemand. CloudStick's per-site PHP-FPM and OPcache tuning panel calculates a safe pm.max_children value from the server's actual RAM automatically, so you do not have to do this math by hand.
OPcache speeds up how fast PHP runs; it does nothing to stop PHP from running at all. If a page's HTML rarely changes between visitors, an Nginx-level page cache can serve it in a few milliseconds without ever touching PHP-FPM or the database, which is the single biggest TTFB win available on most sites.
On a CloudStick server, nginx-cs sits in front and proxies dynamic requests through to apache2-cs on 127.0.0.1:81, which hands off to the site's PHP-FPM pool over a unix socket. Drop a fastcgi_cache block into the site's per-site config at /etc/nginx-cs/extra.d/<site>.d/ to cache the rendered output at that first layer:
For content that genuinely differs per logged-in user — carts, account pages, admin screens — page caching is the wrong tool, but a Redis object cache still helps enormously. Redis sits between PHP and MariaDB, storing the results of repeated database queries in memory, so the second and subsequent requests for the same data skip the query entirely. For WordPress specifically, an object-cache drop-in backed by Redis routinely cuts TTFB on logged-in and dynamic pages by half or more, because most of a WordPress request's time goes into database round trips that Redis eliminates.
Caching hides slow queries; it does not fix them, and on pages that cannot be cached at all, the query itself is your TTFB. Turn on MariaDB's slow query log and let it tell you exactly which query is the problem instead of guessing from the schema.
Run EXPLAIN on any query that shows up repeatedly to see whether it is doing a full table scan instead of using an index — a missing index on a WHERE or JOIN column is the most common cause of a query that takes 400ms instead of 4ms. If queries look fine individually but everything is slow under load, check disk I/O directly with iostat; a %util column pinned near 100 means the disk itself, not MySQL or PHP, is the bottleneck, which usually means the site has outgrown spinning or shared storage and needs faster disks or a smaller working set that fits in RAM.
If your curl breakdown from earlier showed time_namelookup or time_connect as the dominant slice, the fix has nothing to do with PHP or your database — it is DNS or physical distance. A slow or geographically distant DNS resolver adds a fixed tax to every single request before your server ever sees it, and it disproportionately hurts first-time visitors whose resolvers have not cached your domain's records yet.
Moving your DNS to a fast anycast provider and setting a reasonable TTL — not so long that changes take hours to propagate, not so short that resolvers have to re-query constantly — reduces that overhead. Separately, if your visitors are scattered globally but your server sits in one region, the speed of light itself becomes your floor: a visitor in Sydney hitting a server in Virginia will never see sub-100ms TTFB no matter how fast your PHP and database are, because the round trip alone takes longer than that. In that case the real fix is either relocating the origin closer to your primary audience or putting a CDN edge in front of it that can serve cached responses from a nearby point of presence — a separate setup covered in our CDN guide.
Work this in order: measure with curl -w first, then fix whichever slice of the timing breakdown is largest rather than tuning everything at once. A DNS-heavy result means DNS, a connect-heavy result means network path or distance, and a starttransfer-heavy result means server processing — OPcache, PHP-FPM's process manager, page or object caching, and slow queries, roughly in that order of typical impact.
After each change, re-run the same curl command and compare the new numbers against the old ones — TTFB work is only useful when it is measured before and after, not assumed. CloudStick's dashboard makes the server side of this loop faster to watch: the Web Application Logs panel shows per-site errors and slow requests as they happen, and real-time CPU, RAM, and disk graphs make it obvious when a "PHP-FPM tuning" fix has actually pushed a server toward memory pressure instead of relieving it. Fix the biggest slice, remeasure, and move to the next one — most sites need only two or three of the fixes above before TTFB stops being the bottleneck at all.

