VPS Tutorials
High TTFB on a VPS Website (2026): Time to First Byte, Nginx, PHP-FPM, Database, and Cloudflare Troubleshooting
When your VPS website loads slowly but bandwidth isn't saturated and CPU isn't high, check TTFB first. This article walks through the browser waterfall, curl timing, Nginx upstream_time, PHP-FPM, database slow queries, DNS/TLS, and Cloudflare origin fetch in order.

High TTFB on a VPS Website (2026): Time to First Byte, Nginx, PHP-FPM, Database, and Cloudflare Troubleshooting
Many people say "the website is slow," but that isn't really one problem.
Some sites have images that are too large, so pages download slowly; some have too much front-end JS, so the browser renders slowly; and there's a more annoying kind: the page stays blank for a while before anything starts to appear.
In this case, the first thing to look at isn't bandwidth, and it isn't the CDN either — it's TTFB.
TTFB is Time To First Byte. Simply put, it's how long after the browser sends a request it receives the first byte returned by the server.
If TTFB is high, the slowdown is most likely happening in one of these places:
- DNS / TLS / network handshake
- CDN origin fetch
- Nginx reverse proxy
- PHP-FPM / Node / Python application
- Database queries
- Cache misses
- VPS CPU, disk I/O, memory pressure
I once helped someone look at a WordPress site where the homepage TTFB was 2.8 seconds. They had gone through image compression, changing themes, and enabling Brotli, but in the end it turned out a plugin was querying more than 400 database records on every homepage load. After dealing with the slow queries and object caching, TTFB dropped to around 300ms.
So this article isn't about "ten tips to speed up your website" — it's about one thing: how to pinpoint which layer is stuck when TTFB is high.
Don't judge by feel.
Open developer tools in your browser, go to Network, refresh the page, and look at the Timing of the main HTML request.
Focus on:
| Phase | Description |
|---|---|
| DNS Lookup | Domain resolution time |
| Initial connection | TCP connection time |
| SSL | TLS handshake time |
| Waiting for server response | Usually the core part of TTFB |
| Content Download | Content download time |
If Content Download is what's slow, the page may be too large, images too large, or bandwidth slow.
If Waiting for server response is what's slow, that's the TTFB problem this article addresses.
You can also measure with a command:
curl -o /dev/null -s -w 'dns:%{time_namelookup}\nconnect:%{time_connect}\ntls:%{time_appconnect}\nttfb:%{time_starttransfer}\ntotal:%{time_total}\n' https://example.com/
The output might be:
dns:0.012
connect:0.080
tls:0.160
ttfb:2.431
total:2.620
This makes it obvious: what's actually slow is the first byte, not the download.
High TTFB doesn't necessarily mean the server is slow — it could also be a network path issue.
Test from at least two places:
curl -o /dev/null -s -w '%{time_starttransfer}\n' https://example.com/
You can test from:
- Your local computer
- The VPS itself
- A server in another region
- The Cloudflare dashboard / WebPageTest / PageSpeed Insights
If the VPS itself accesses it quickly:
curl -o /dev/null -s -w '%{time_starttransfer}\n' http://127.0.0.1/
But your local access is slow, it could be:
- Slow DNS resolution
- Slow cross-border network path
- Slow CDN node origin fetch
- Network jitter during peak hours
If the VPS itself is also slow, the problem is most likely in the application, Nginx, the database, or machine resources.
For peak-hour issues, see: Why VPS Gets Slow During Peak Hours.
If you use Cloudflare or another CDN, don't rush to blame the origin.
Test two paths separately:
- User accesses the CDN
- CDN fetches from the VPS origin
If the domain goes through Cloudflare, you can temporarily test with the origin IP:
curl -H 'Host: example.com' -o /dev/null -s -w 'ttfb:%{time_starttransfer} total:%{time_total}\n' http://YOUR_ORIGIN_IP/
If the origin is fast but going through Cloudflare is slow, it could be:
- Slow network path from the CDN node to the origin
- Cloudflare security rules / WAF inspection taking time
- Cache rules not matching
- Problems with origin protocol, TLS, or redirect configuration
If the origin itself is slow, the CDN is just exposing the slowness.
For Cloudflare origin issues, see: What to Do About Cloudflare 521 / 522 on a VPS Website.
Often you need to know: is the slowness in Nginx, or in the application behind Nginx?
Nginx's default log doesn't necessarily have these fields, so it's worth temporarily adding a more useful log_format:
log_format timing '$remote_addr - $host "$request" '
'status=$status request_time=$request_time '
'upstream_response_time=$upstream_response_time '
'upstream_connect_time=$upstream_connect_time '
'upstream_header_time=$upstream_header_time';
access_log /var/log/nginx/access_timing.log timing;
Check the configuration:
nginx -t
systemctl reload nginx
Then visit the page a few times and check again:
tail -f /var/log/nginx/access_timing.log
You might see:
status=200 request_time=2.845 upstream_response_time=2.801 upstream_header_time=2.801
This means most of the time is spent in the upstream application.
If it's:
request_time=2.800 upstream_response_time=-
Then it could be static files, Nginx handling it itself, connection waiting, or other configuration issues.
For Nginx configuration errors, see: What to Do About VPS Nginx Configuration Errors.
If you run a PHP site like WordPress, Laravel, or Typecho, high TTFB often comes from PHP-FPM.
First check the service status:
systemctl status php8.2-fpm
Check the logs:
journalctl -u php8.2-fpm -n 100 --no-pager
Adjust the version number to match your system, e.g. php8.1-fpm, php8.3-fpm.
Focus on these signals:
server reached pm.max_children setting
child exited on signal
slow request
pool www seems busy
If you see pm.max_children maxed out, it means there aren't enough PHP workers or individual requests are too slow.
You can check the process count:
ps aux | grep php-fpm | wc -l
You can also enable the PHP-FPM slowlog:
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
Then reload PHP-FPM.
For slow WordPress sites, continue reading: What to Do When WordPress Is Slow on a VPS.
If the page queries the database every time, a slightly slow database will directly push TTFB up.
For MySQL / MariaDB, first check whether the slow query log is enabled:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
You can enable it temporarily like this:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
Then check the slow query log location:
SHOW VARIABLES LIKE 'slow_query_log_file';
Common problems:
- Missing indexes
- Plugins or themes making duplicate queries
- Homepage querying too much data at once
- Database and application not in the same region
- Insufficient database connection pool
- Tables too large without pagination
If you don't want to analyze SQL right away, at least check whether the database is under pressure:
top
iotop -o
mysqladmin processlist
Database connection failures or startup failures are a different problem; see: What to Do When VPS MySQL / MariaDB Won't Connect.
Many small sites have high TTFB not because the machine is weak, but because caching isn't configured well.
You can first check the response headers:
curl -I https://example.com/
Pay attention to these:
cache-control
cf-cache-status
x-cache
server-timing
If you use Cloudflare but HTML pages always show:
cf-cache-status: DYNAMIC
That means Cloudflare isn't caching HTML and fetches from the origin every time.
This is normal for admin panels, shopping carts, and login pages; but for pure content pages, blog pages, and documentation pages, you can consider caching.
Nginx can also use fastcgi_cache or proxy_cache, but be careful:
- Don't cache logged-in users
- Don't cache the admin panel
- Don't carelessly cache pages with user-state cookies
- Don't cache form, payment, or comment submissions
Getting caching wrong is more troublesome than not doing it.
Many people look at top, see CPU isn't high, and assume the machine is fine.
But high TTFB can be stuck on:
- Disk I/O
- iowait
- Memory reclaim
- Swap thrashing
- PHP-FPM worker queuing
- Database lock waits
Check system load:
uptime
Check CPU, memory, iowait:
vmstat 1
Check disk I/O:
iostat -xz 1
Check processes:
htop
If wa is high, or disk util stays near 100% for a long time, requests may all be waiting on disk.
In this case, see: What to Do When VPS Disk I/O Is Very High.
TTFB is measured from when the request starts to the first byte, and sometimes DNS, connection, and TLS are already slow before that.
You can break it down with curl:
curl -o /dev/null -s -w 'dns:%{time_namelookup}\nconnect:%{time_connect}\ntls:%{time_appconnect}\nttfb:%{time_starttransfer}\ntotal:%{time_total}\n' https://example.com/
If dns alone is 0.8 seconds, that's not PHP being slow.
If tls is high, it could be:
- Certificate chain issues
- Multiple HTTPS redirects
- Inappropriate SSL mode between origin and CDN
- Abnormal HTTP/2 / HTTP/3 configuration
Check the redirect chain:
curl -IL https://example.com/
If you see back-and-forth redirects like http -> https -> www -> non-www -> https, TTFB will also look bad.
For HTTPS redirect loops, see: What to Do About HTTPS Redirect Loops on a VPS Website.
A normal homepage TTFB doesn't mean the endpoints are normal.
What's actually slow on many sites is:
/api/search/wp-admin/admin-ajax.php/api/orders/graphql- Login endpoints
- Admin list pages
Test them separately:
curl -o /dev/null -s -w 'ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com/api/search
If only one endpoint is slow, don't optimize the whole site blindly. Check that endpoint directly:
- Does it query the database too much
- Does it call external APIs
- Is there no caching
- Is there lock waiting
- Is it being rate-limited or inspected by WAF
I've seen a site where the homepage was fast but the search endpoint had a TTFB of 6 seconds, and it turned out every search made a real-time request to a third-party API. No matter how expensive a VPS you switch to, you'll only turn 6 seconds into 5.8 seconds.
You can follow this order:
# 1. 拆开 DNS、连接、TLS、TTFB、总耗时
curl -o /dev/null -s -w 'dns:%{time_namelookup}\nconnect:%{time_connect}\ntls:%{time_appconnect}\nttfb:%{time_starttransfer}\ntotal:%{time_total}\n' https://example.com/
# 2. 在 VPS 本机测源站
curl -o /dev/null -s -w 'ttfb:%{time_starttransfer} total:%{time_total}\n' http://127.0.0.1/
# 3. 绕过 CDN 测源站 IP
curl -H 'Host: example.com' -o /dev/null -s -w 'ttfb:%{time_starttransfer} total:%{time_total}\n' http://YOUR_ORIGIN_IP/
# 4. 看 Nginx 访问时间字段
tail -f /var/log/nginx/access_timing.log
# 5. 看应用日志
journalctl -u php8.2-fpm -n 100 --no-pager
# 6. 看系统资源
vmstat 1
iostat -xz 1
Then judge by the results:
| Symptom | What to check first |
|---|---|
| Slow DNS | DNS provider, resolution path, local network |
| Slow TLS | Certificate chain, redirects, CDN SSL mode |
| Origin itself is also slow | Application, PHP-FPM, database, I/O |
| Origin fast, CDN slow | CDN origin fetch, cache rules, WAF |
| High upstream_response_time | Backend application or database |
| High request_time but low upstream | Nginx, client, download, rate limiting |
| One endpoint slow | Check endpoint logic, SQL, external APIs |
High TTFB can of course mean the machine is too weak, but that's not the first conclusion.
Situations where upgrading the machine makes sense:
- CPU maxed out for long periods
- Insufficient memory causing frequent swap
- Disk I/O maxed out for long periods
- Database already optimized but resources insufficient
- PHP-FPM workers still queuing after reasonable configuration
Situations where upgrading won't solve it:
- A plugin writing slow SQL
- Calling an external API on every request
- Cloudflare not caching static content
- Nginx reverse proxy misconfiguration
- Messy redirect chains
- Database missing indexes
Upgrading the VPS sometimes just pushes the problem back a month. What you really should do is first pinpoint which layer is slow.
If you're troubleshooting website TTFB, here are a few articles to read next:
- What to Do When WordPress Is Slow on a VPS
- What to Do About 502 / 504 Bad Gateway on a VPS
- What to Do About VPS Nginx Configuration Errors
- What to Do When VPS Disk I/O Is Very High
- What to Do About Cloudflare 521 / 522 on a VPS Website
Finally, the most practical judgment: if time_starttransfer is high in curl but time_total isn't much higher than it, focus on the backend generating the page; if time_starttransfer isn't high but time_total is, focus on download size, images, bandwidth, and front-end resources. Distinguish these two first, and you'll avoid most of the detours.