Optimizing PHP-FPM Performance for High-Traffic WordPress Sites
A busy WordPress site rarely slows down because PHP-FPM has one bad setting. The usual cause is a queue of small constraints: limited memory, slow database queries, uncached requests, excessive plugins, and a web server accepting more work than the application can complete. PHP-FPM tuning works best when it addresses that whole path.
This matters to Australian publishers, retailers and membership organisations serving visitors from Perth to Brisbane. A campaign launched during an afternoon in Sydney may coincide with evening traffic in Western Australia, while a promotion aimed at local customers can produce a sharp burst rather than a steady increase. NBN performance improves the visitor’s connection, but it cannot compensate for an overloaded origin server.
The goal is controlled concurrency. The server should run enough PHP workers to keep useful requests moving without exhausting RAM and forcing Linux to swap. A smaller, well-measured pool will usually outperform an oversized pool that spends its time competing for CPU, database connections and memory.
Treat every change as an operational experiment. Record baseline latency, request volume, error rates and resource use, change one related group of settings, then compare the result during a realistic traffic period. That discipline is more valuable than copying a pool configuration from a much larger American hosting environment.
Measure The Request Path First
Begin with the web server, PHP-FPM, WordPress and database as separate stages. Track time to first byte, PHP execution time, upstream response time, 5xx errors, active FPM processes and requests waiting in the FPM listen queue. A reverse proxy or CDN can make average page speed look excellent while uncached checkout or login requests remain painfully slow.
Enable PHP-FPM status reporting on an internal address or through an authenticated monitoring route. The important figures include active processes, idle processes, maximum active processes and the number of times the pool reached its limit. The max children reached counter is a useful warning, though it does not prove that adding workers is the right answer.
Use access logs to separate cache hits from dynamic requests. A WordPress homepage delivered from FastCGI cache has a very different resource profile from an administrator saving a post, a WooCommerce customer checking out, or a REST API request generated by a mobile application. Measure p95 and p99 latency, not just the average, because the slow tail is where visitors abandon a page.
Size The Pool Around Memory
The central PHP-FPM setting is pm.max_children. It defines the maximum number of simultaneous PHP workers, so it must be based on memory available to PHP rather than on the number of CPU cores alone. A server with eight cores and insufficient RAM can become less responsive when given hundreds of workers.
Estimate the resident memory of several representative PHP-FPM processes after the site has warmed up. Reserve RAM for the operating system, web server, database, object cache and monitoring agents. Then divide the remaining safe PHP budget by the larger observed worker size, adding a margin for traffic spikes. WordPress plugins can cause substantial variation, particularly page builders, image processors and commerce extensions.
Use pm = dynamic when the site has a variable workload and you want idle workers ready for ordinary bursts. pm = ondemand can reduce memory use on low-volume sites, although it may add process-start latency during a sudden campaign. pm = static provides predictable concurrency and can be effective on a dedicated application host, but only after memory use is well understood.
A sensible starting point might be 10 to 20 workers on a modest virtual machine, followed by testing. That is an example, not a universal recommendation. If workers are constantly saturated while CPU remains mostly idle, investigate slow database calls, external HTTP requests and cache misses before simply raising the limit.
Build A Safe Pool Configuration
Keep pool settings explicit and document why each value exists. pm.max_requests periodically recycles workers, which can contain gradual memory growth from extensions or plugins. Set it to a moderate value such as 300 to 1,000 and watch whether recycling causes noticeable CPU spikes. request_terminate_timeout can stop a request that has become stuck, but it should complement investigation rather than hide a broken integration.
Useful PHP-FPM controls include:
pm.max_childrento cap concurrent PHP executionpm.max_requeststo recycle long-lived workersrequest_slowlog_timeoutto identify slow scriptslisten.backlogto hold a short burst of waiting connectionspm.status_pathfor pool health and capacity metrics
The listen backlog is not extra application capacity. It only provides a temporary queue, and a very large backlog can conceal saturation while visitors wait longer. Configure the socket permissions carefully, use Unix sockets when the web server shares the host, and place logs where rotation will prevent disk exhaustion.
Set memory_limit high enough for legitimate WordPress operations but not so high that one faulty request can consume the machine. Uploads, backups and image transformations may need a separate operational path or scheduled worker rather than unrestricted web requests. On a multi-site installation, separate pools can isolate a noisy tenant, though that adds configuration and monitoring overhead.
Reduce Work Before Adding Workers
The fastest PHP request is the one that never reaches PHP. Full-page caching can serve anonymous WordPress pages without starting a worker, while a CDN can deliver static assets from a nearby edge location. For Australian audiences, an edge presence in Sydney or Melbourne can reduce round-trip time, but the origin still needs enough capacity for cache misses and personalisation.
Use persistent object caching with Redis or Memcached when repeated database reads are expensive. Review cache keys and invalidation behaviour carefully: stale product prices, stock levels or event details can be worse than a slower page. WooCommerce carts, logged-in dashboards and previews usually need different rules from public articles.
Audit plugins and themes with Query Monitor, application performance monitoring or database slow-query logs. Look for repeated options queries, unbounded LIKE searches, remote API calls during page generation and scheduled tasks running inside visitor requests. WordPress cron can create a surprising load pattern; disabling web-triggered cron and running it through the system scheduler often makes traffic behaviour easier to control.
Keep PHP and extensions current, but test upgrades in staging. PHP 8.x can improve execution speed over older releases, while an incompatible plugin can create fatal errors that look like an infrastructure failure. For a broader view of storage behaviour and redundancy, the discussion of ZFS storage design offers useful infrastructure context, even though file storage and PHP execution are different concerns.
Use Caching Without Breaking Dynamic Pages
Caching needs an explicit policy. Cache public HTML for a short period, purge affected URLs after publishing, and bypass the cache for administrators, carts and authenticated users. Avoid blanket exclusions that send every query-string request to PHP; identify which parameters genuinely change content and which are merely tracking tags.
A practical cache stack can include:
- Browser caching for versioned CSS, JavaScript and images
- CDN caching for public static assets and selected HTML
- FastCGI or reverse-proxy caching for anonymous pages
- Redis or Memcached for repeated WordPress object lookups
- OPcache for compiled PHP bytecode
Enable OPcache with enough memory and interned-string storage for the application, then monitor hit rate and wasted memory. In a deployment pipeline, use timestamp or atomic release strategies so workers do not execute a mixture of old and new files. A restart is not always needed for every deployment, but stale bytecode behaviour should be understood.
Do not assume a cache plugin automatically provides a complete caching strategy. Confirm cache headers, purge events, cookie handling and the treatment of REST, AJAX and preview requests. Test from an external Australian connection and from a logged-in session, because a cached public page may hide a slow origin path that editors and customers still experience.
Monitor Capacity And Deploy Gradually
Alert on symptoms that indicate user impact: rising p95 latency, a growing FPM queue, repeated pool saturation, database connection exhaustion, increasing swap activity and elevated 502 or 504 responses. Correlate these signals with traffic, deployments, scheduled jobs and third-party services. A brief CPU spike during image processing is different from a sustained queue caused by every request waiting on MySQL.
Log slow requests with enough context to find the responsible route, but avoid recording passwords, payment details or unnecessary personal information. Apply Australian privacy and security expectations to logs, backups and monitoring exports, particularly when a site handles customer accounts or health-related data.
Make changes in small steps and retain a rollback path. During a high-profile sale, EOFY promotion or local event launch, freeze risky configuration work and ensure someone can respond during the relevant AEST and AWST windows. A runbook should include pool restart commands, recent configuration versions, cache purge procedures and the point at which traffic should be diverted or the campaign paused.
For teams maintaining several servers, a concise PHP operations reference can complement vendor documentation, but production values should still be validated against the versions and workload in use. Record capacity tests in the same repository as infrastructure configuration so future tuning starts with evidence rather than folklore.
Apply the work in order: measure the request path, protect memory, remove avoidable PHP execution, establish cache rules, and monitor the result. When the pool is sized from real worker usage and tested against realistic bursts, a WordPress site can handle Australian traffic peaks with fewer surprises and a much clearer path to its next capacity increase.
Karl Katzke