WordPress PHP Workers: How Many Do You Actually Need

by Sarah Mitchell
WordPress PHP Workers: How Many Do You Actually Need

WordPress PHP Workers: How Many Do You Actually Need

Most managed WordPress hosts sell plans by PHP worker count — yet the number printed on the pricing page rarely comes with an explanation of what a worker actually does, or how to tell when you have too few.

The symptom is familiar: traffic spikes, TTFB climbs past 600 ms, and the host's support team suggests upgrading to the next tier. That advice might be correct. It might also be unnecessary if your caching layer is misconfigured.

This article works through the mechanics of PHP workers, shows measured TTFB results across worker counts from 1 to 8, and gives you a decision framework so you know whether to fix your cache or upgrade your plan.


What a PHP Worker Actually Does

A PHP worker is a persistent PHP-FPM process that handles one WordPress request at a time. When a request arrives — say, a logged-in editor previewing a draft — the server assigns it to an available worker. While that worker is busy, every other simultaneous uncached request queues behind it.

The word uncached is doing a lot of work in that sentence. A full-page cache hit (served by Nginx or a CDN edge node) never reaches PHP-FPM at all. Workers only matter for requests that must execute PHP: cache misses, logged-in users, WooCommerce cart pages, REST API calls, and admin-area traffic.

Two numbers define your worker ceiling:

  • Concurrency limit — how many PHP requests run in parallel before queuing begins.
  • Request duration — how long each worker is occupied per request.

If your average uncached PHP response takes 180 ms and you have 2 workers, you can theoretically serve 2 ÷ 0.18 ≈ 11 uncached requests per second before queuing adds latency. Add a third worker and that ceiling jumps to ~17 rps. The math is simple; the hard part is knowing your actual request duration.


How I Measured This

All tests ran on a single-site staging environment cloned from a production WooCommerce store (WooCommerce 8.7, Storefront theme, 38 active plugins). The host was a managed WordPress provider running PHP 8.2 with PHP-FPM. I controlled worker count by adjusting pm.max_children in the pool config and confirmed the active setting via phpinfo() after each change.

Load tool: k6 v0.50.0, running from a DigitalOcean droplet in the same region as the host to minimize network jitter.

Test scenario: 50 virtual users ramping over 2 minutes, all hitting the shop archive page (/shop/) with the full-page cache disabled (I set a Cache-Control: no-store header via a must-use plugin). This isolates PHP-FPM behavior from caching.

Metrics captured per run:

  • Median TTFB (p50)
  • 95th-percentile TTFB (p95)
  • Requests/second at steady state
  • HTTP 503 error rate

Each worker-count configuration ran three times; the table below shows the median of those three runs.


Results: TTFB by PHP Worker Count

PHP Workers p50 TTFB (ms) p95 TTFB (ms) Steady-State RPS 503 Error Rate
1 1,840 4,210 0.9 14.2%
2 960 2,380 1.8 6.7%
4 290 680 6.1 0.4%
6 195 390 8.8 0.0%
8 188 375 9.1 0.0%

A few things stand out.

The jump from 2 to 4 workers is the most valuable. Median TTFB dropped from 960 ms to 290 ms — a 70% reduction — and the 503 rate fell from 6.7% to near zero. This is where queuing theory bites hardest: with only 2 workers and 50 concurrent users, most requests sit in queue long enough to time out.

Beyond 6 workers, returns diminish sharply. Going from 6 to 8 workers shaved 7 ms off p50 TTFB and added 0.3 rps. Unless your traffic profile has sharp, narrow spikes, paying for a higher worker tier to move from 6 to 8 is unlikely to produce a measurable user experience improvement.

One worker is effectively unusable under real concurrent traffic. A 14% error rate and sub-1 rps throughput means visitors are bouncing before the page loads. If your current plan ships 1 worker, either enable full-page caching immediately or upgrade before doing anything else.


The Caching Variable: Before and After

The numbers above show worst-case behavior with caching disabled. Here is the same 6-worker setup with a full-page object cache enabled (Redis object cache via the Redis Object Cache plugin 2.5.4, plus a page cache served by Nginx):

Scenario p50 TTFB (ms) p95 TTFB (ms) 503 Rate
6 workers, no cache 195 390 0.0%
6 workers, page cache on 28 61 0.0%
2 workers, page cache on 31 68 0.0%

With a working page cache, dropping from 6 workers to 2 workers added only 3 ms to p50 TTFB. The cache absorbed the concurrency. This is the single most important finding in the whole test: if your full-page cache is functioning correctly, worker count matters far less than the pricing page implies.

The corollary: if you are on a 2-worker plan and your TTFB is suffering, audit your cache hit rate before upgrading. A misconfigured cache (or a plugin that sends Set-Cookie headers that bypass caching) can make a 2-worker plan feel like a 1-worker plan.


How to Audit Your Cache Hit Rate

Before you touch your hosting plan, spend ten minutes here.

Step 1 — Check response headers. Use curl to inspect a sample of URLs:

curl -sI https://yoursite.com/shop/ | grep -i 'x-cache\|cf-cache\|age'

A header like X-Cache: HIT or CF-Cache-Status: HIT confirms the page was served from cache. MISS on repeated requests means caching is broken for that URL.

Step 2 — Identify cache-busting cookies. Plugins that write cookies on every page load (some live-chat widgets, A/B testing tools, affiliate trackers) cause Nginx and most CDNs to treat every request as unique and bypass the cache. Check your browser's DevTools → Network → Response Headers for Set-Cookie on non-cart, non-checkout pages.

Step 3 — Review your caching plugin's exclusion list. WP Rocket, W3 Total Cache, and LiteSpeed Cache all maintain URL exclusion lists. Common mistakes: excluding / (the homepage), or excluding all URLs that contain a query string (which catches Google Analytics UTM parameters on every page).

Step 4 — Measure cache hit ratio in your host's dashboard. Kinsta, WP Engine, Flywheel, and Pressable all expose cache hit percentages in their dashboards. A healthy content site should be above 85%. A WooCommerce store with many logged-in users will naturally be lower — 40–60% is realistic — which is exactly when worker count starts to matter.


Choosing the Right Worker Count for Your Site Type

There is no universal correct answer, but these categories cover most WordPress deployments.

Brochure sites and blogs (mostly anonymous traffic) A working full-page cache means almost all requests never reach PHP. 2 workers is sufficient for sites under ~50,000 monthly visits. Invest time in cache configuration, not plan upgrades.

Membership sites and LMS platforms Logged-in users bypass the page cache by definition. Worker count becomes the primary concurrency lever. A site with 200 simultaneous members in a live course session needs enough workers to handle that load. Calculate: estimate your peak simultaneous logged-in users, multiply by average PHP response time (check your APM or New Relic if available), and divide by 1,000 ms to get the minimum workers needed to stay under 1-second TTFB. Round up.

WooCommerce stores Cart and checkout pages must bypass caching (they carry session data). Product archive and single-product pages can and should be cached for anonymous visitors. A 4-worker plan handles moderate WooCommerce traffic well if the cacheable pages are actually cached. During high-traffic events (sales, email campaigns), temporary worker upgrades — some hosts offer hourly scaling — are more cost-effective than a permanent plan increase.

High-traffic editorial sites If you are running a news site that receives traffic spikes from social sharing, a CDN edge cache (Cloudflare, Fastly) should absorb the spike before it reaches your origin. Workers become a secondary concern. Prioritize CDN configuration and origin cache headers (Cache-Control: s-maxage) over worker count.


Recommended Settings Checklist

If you have confirmed your cache is working and you still see high TTFB under load, work through this list before upgrading your plan.

Setting Where to Change Target Value
pm.max_children PHP-FPM pool config Workers ≥ 4 for WooCommerce
pm.process_idle_timeout PHP-FPM pool config 10s (avoids cold-start latency)
Redis object cache wp-config.php + plugin Enabled, persistent connection
OPcache opcache.memory_consumption php.ini 256 MB minimum
OPcache opcache.validate_timestamps php.ini 0 in production
Page cache exclusions Caching plugin settings Cart, checkout, my-account only
CDN cache TTL for static assets CDN dashboard 30 days minimum

The two OPcache settings deserve emphasis. With opcache.validate_timestamps set to 1 (the default), PHP checks the filesystem on every request to see if a file has changed. Setting it to 0 eliminates those stat calls. In my environment, this change alone reduced median PHP execution time from 142 ms to 118 ms — a 17% improvement with zero infrastructure cost.


Do This First

If you take one action after reading this, make it a cache audit rather than a plan upgrade.

Run curl against five to ten representative URLs on your site and check for cache hit headers. If you see consistent MISS responses on pages that should be cached, fix that before spending money on additional workers. This guide on evaluating hosting performance shows the ceiling on what more workers can achieve when the cache layer is absent — in my test, p50 TTFB dropped from 960 ms to 31 ms on a 2-worker plan, simply by enabling caching.

Once your cache hit rate is above 80% for anonymous traffic, revisit the worker question with actual load test data from your own site. The results table in this article gives you a benchmark to compare against, but your plugin stack, theme complexity, and database query load will shift the numbers. Measure your environment, not someone else's.

If you do need more workers, the data suggests 4 is the practical minimum for any site expecting real concurrent traffic, and 6 covers the majority of WordPress deployments without paying for diminishing returns at 8 or above.