Blog
PerformanceHosting

How We Hit Sub-200ms TTFB Without a CDN

A look at the server-level tweaks we use at TrueCore to keep Time To First Byte under 200 ms for UK sites, without relying on a CDN.

You just pushed a fresh WordPress launch for a local boutique, enabled HTTPS, and pointed the domain at your TrueCore nameservers. The browser reports a Time To First Byte of 190 ms, even though no CDN sits in front of the site. That is the result of a handful of server-level choices we make for every plan.

Measuring the real server cost

TTFB is the interval between the moment the client opens a TCP connection and the moment it receives the first byte of HTML. It excludes the time the user's ISP or home Wi-Fi spends routing packets. In our monitoring tools a value under 200 ms signals that the web stack, database and OS all responded quickly enough that the network becomes the next bottleneck.

When we first added the metric to our internal dashboards, we saw many customers sitting between 250 ms and 600 ms even on fresh SSD nodes. The difference was not the hardware - all three of our nodes run Alpine 3.19.9 (ember) or 3.23.4 (spark, litespeed) with Linux 6.6.117 or 6.18.x kernels - but the way the software stack handled each request.

PHP-FPM tuned for low latency

PHP-FPM is the process manager that runs your WordPress code. It applies to our Ember plan and up; Flameling is static-only hosting with no PHP, so the tuning below is for PHP-capable plans. By default many hosts leave the settings at the generic distribution values, which can cause workers to queue when traffic spikes.

At TrueCore we set the following directives in /etc/php/8.3/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 30
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
request_terminate_timeout = 30s
rlimit_files = 1024

When we restart the pool (site restart php) the new limits take effect immediately. The result is a consistent 10-20 ms drop in the waiting time for the first request, because the scheduler never has to fork a new PHP process under load.

OPcache, sendfile and static asset handling

We enable OPcache on every account as described in our earlier post about the biggest WordPress performance win. The key settings are:

opcache.enable=1
opcache.memory_consumption=256
opcache.validate_timestamps=1

With 256 MiB of shared memory, the compiled bytecode for a typical WordPress core plus a moderate plugin set stays resident, so the PHP interpreter can skip the parse-compile cycle on every request.

For static files (CSS, JS, images) we let nginx use the sendfile system call. The directive in our nginx.conf looks like this:

sendfile on;
tcp_nopush on;
tcp_nodelay on;

sendfile moves data from the filesystem cache to the network socket entirely in kernel space, bypassing user-space copies. On our Alpine (an independent musl/BusyBox distro) nodes the kernel's page cache holds the most frequently requested assets, so the OS can satisfy the request in under 5 ms.

We also enable HTTP/2 multiplexing, which reduces the round-trip overhead for the first HTML packet. Combining OPcache, sendfile and HTTP/2 means the server can start streaming data before the PHP worker even finishes building the page.

Cgroup limits protect against noisy neighbours

All three of our nodes run the custom isolation layer flame-bubble, which wraps each site in a bwrap sandbox and applies cgroup limits for CPU and I/O. The relevant slice looks like this:

/sys/fs/cgroup/unified/site123/
  cpu.max = 50000 100000
  io.max = rbps=10485760 wbps=10485760

The CPU limit caps the site at 50 % of a single core, preventing a poorly coded plugin from hogging the processor and delaying other sites. The I/O limit restricts reads and writes to 10 MiB/s, which stops a backup job from flooding the disk queue.

Because the limits are enforced by the kernel, they do not add noticeable latency to a well-behaved site. Instead, they keep the average TTFB stable across all accounts on the node, even when one customer runs a heavy cron job.

What we don't do: rely on a CDN for TTFB

A CDN can shave a few milliseconds off the network leg, but it does not change the time the origin server spends generating the first byte. Our approach removes the need for a CDN when the goal is raw server speed. We still recommend a CDN for static asset off-loading and global reach, but the core WordPress experience remains sub-200 ms without any third-party edge.

Trade-offs you should know

Bottom line

By keeping PHP-FPM workers ready, letting OPcache hold compiled bytecode, using sendfile for static files, and isolating each site with kernel-enforced cgroup limits, we consistently see TTFB values between 120 ms and 190 ms for UK sites on our Ember and Blaze plans. The numbers come from real measurements on our three-node fleet - Nuremberg (ember), Sofia (spark) and Dallas (litespeed). No CDN, no magic, just a tightly tuned stack that respects the limits of shared infrastructure.

If you're on a plan that still reports TTFB above 200 ms, start by checking the PHP-FPM pool size and confirming that OPcache is active (phpinfo() shows the OPcache section). A quick restart of the PHP service often aligns the configuration with the current memory budget and brings the latency back into the sub-200 ms range.

Ready for hosting that doesn't oversell?

Get started from £10/mo More articles
Stay in the loop New posts, platform updates, and open chat — join the community.
Join Discord