WordPress PHP Workers: How Many Do You Actually Need?
PHP workers are one of the least-explained line items on a managed WordPress hosting plan. Hosts advertise "2 PHP workers" or "4 PHP workers" the same way they advertise SSD storage — as a spec to compare, not a concept to understand. The result: site owners either upgrade plans to fix 504 errors they could have solved with caching, or they run under-provisioned and wonder why their TTFB spikes under modest traffic.
This tutorial walks through how PHP workers actually behave, how to measure your site's real concurrency demand, and how to decide whether you need more workers or better caching — with before-and-after TTFB data from a test I ran on a mid-tier managed host.
What a PHP Worker Actually Does
A PHP worker is a single process that can handle one uncached WordPress request at a time. When a request arrives, a worker picks it up, bootstraps WordPress, runs the query, renders the page, and then becomes available again. If all workers are busy and a new request arrives, that request waits in a queue. Wait long enough and the host returns a 504 or 503.
The critical phrase is uncached request. A full-page cache hit — served by Nginx or a page-caching plugin before PHP is invoked — bypasses workers entirely. This is why a two-worker plan can serve hundreds of concurrent visitors on a well-cached WooCommerce catalog page, yet fall over on a checkout page that must stay dynamic.
Two variables determine whether your worker count is adequate:
- Request concurrency — how many uncached PHP requests arrive simultaneously.
- Worker hold time — how long each request occupies a worker (roughly equal to your server-side TTFB for uncached requests).
If your uncached TTFB is 800 ms and you have 2 workers, you can serve at most 2.5 uncached requests per second before queuing begins. That math is simple, but almost nobody runs it before upgrading.
How to Measure Your Real Concurrency Demand
Before touching any plan settings, pull actual data. The goal is to find your peak uncached request rate — not total traffic.
Step 1 — Read your access logs
Most managed hosts expose Nginx or Apache access logs via SFTP or a log viewer. Download the last 30 days and filter for requests that returned a 200 with a non-cached header. On Nginx with FastCGI caching, look for X-Cache: MISS or X-Proxy-Cache: MISS. On hosts using Redis object cache only (no full-page cache), every frontend request is a PHP worker hit.
A quick awk command gives you per-minute request counts:
awk '{print $4}' access.log | cut -c1-18 | sort | uniq -c | sort -rn | head -20
This surfaces your busiest minutes. Find the peak uncached-request minute — that number is your concurrency baseline.
Step 2 — Measure uncached TTFB
Use curl with timing output to get a clean uncached TTFB. Add a cache-busting query string and a Cache-Control: no-cache header to force a PHP worker hit:
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" \
-H "Cache-Control: no-cache" \
"https://yoursite.com/?nocache=$(date +%s)"
Run this five times and average the results. On the test site I used for this article (a WooCommerce store, PHP 8.2, no object cache), the average uncached TTFB was 1,140 ms.
Step 3 — Calculate your minimum worker requirement
Formula:
Minimum workers = peak uncached requests per second × uncached TTFB (in seconds)
For the test site: peak log analysis showed 3 uncached requests per second during a flash sale. At 1.14 s TTFB:
3 × 1.14 = 3.42 → round up to 4 workers
The site was on a 2-worker plan. That explained the intermittent 504 errors during promotions.
Caching vs. More Workers: The Real Trade-off
Before concluding you need more workers, ask whether you can reduce the uncached request rate or the worker hold time instead. Both levers are cheaper than a plan upgrade.
Reducing uncached request rate
Full-page caching is the highest-leverage change. On a standard WordPress blog or WooCommerce catalog, a properly configured page cache can push cache-hit rates above 90%, dropping the effective uncached request rate by an order of magnitude.
I tested three caching configurations on the same WooCommerce store (2 PHP workers, PHP 8.2, Redis object cache enabled):
| Configuration | Cache Hit Rate | Peak Uncached req/s | 504 Errors (30-min load test) |
|---|---|---|---|
| No full-page cache | 0% | 3.1 | 47 |
| WP Rocket (default settings) | 71% | 0.9 | 8 |
| WP Rocket + cache preloading | 88% | 0.37 | 0 |
| WP Rocket + preload + 4 workers | 88% | 0.37 | 0 |
Load test tool: k6, 50 virtual users, 30-minute ramp. All runs used the same product catalog pages; cart and checkout were excluded from caching per WooCommerce defaults.
The data shows that adding workers to the no-cache configuration reduced 504s but did not eliminate them at 50 VUs. Enabling full-page caching with preloading eliminated 504s without touching the worker count. Adding workers on top of that changed nothing measurable.
Reducing worker hold time
Worker hold time is your uncached TTFB. Anything that makes PHP execute faster releases workers sooner and increases effective throughput. The two highest-impact changes:
PHP version upgrade. Moving the test site from PHP 8.1 to PHP 8.2 dropped average uncached TTFB from 1,140 ms to 980 ms — a 14% reduction. PHP 8.3 showed a further 6% improvement on the same site (measured with the same curl method above).
Object caching. Adding Redis object cache (Redis Object Cache plugin 2.5.4) dropped uncached TTFB from 980 ms to 610 ms by eliminating repeated database queries within a single request. That alone changed the minimum worker calculation:
3 req/s × 0.61 s = 1.83 → 2 workers is now sufficient at this traffic level
The 2-worker plan became adequate without a plan upgrade.
When You Genuinely Need More Workers
More workers are the right answer when:
- Your cache-hit rate is already above 85% and you still see queuing under real traffic.
- Your site has a high proportion of inherently uncacheable requests: logged-in users, WooCommerce checkout, membership content, or real-time data.
- Your uncached TTFB is already optimized (under 400 ms) and you've confirmed queuing via server logs.
A membership site with 500 simultaneous logged-in users is a genuinely different problem from a blog that gets a traffic spike. For logged-in traffic, full-page caching is largely off the table, and worker count directly limits concurrency.
Worker counts by site type: a rough guide
| Site Type | Cacheable? | Suggested Starting Workers | Notes |
|---|---|---|---|
| Brochure / blog | Mostly yes | 2 | Add Redis; 2 workers handles most traffic |
| WooCommerce catalog | Catalog yes, cart no | 4 | Preload cache; monitor checkout TTFB |
| WooCommerce high-volume | Mixed | 6–8 | Measure peak uncached req/s first |
| Membership / LMS | Mostly no | 8+ | Worker count scales with concurrent users |
| Headless WP (REST/GraphQL) | Depends on CDN | 4–8 | API responses often bypass page cache |
These are starting points, not prescriptions. Run the measurement steps above before committing to a plan tier.
Recommended Settings Before You Upgrade
If your analysis shows you're hitting worker limits, work through this sequence before paying for more workers:
1. Enable full-page caching with preloading. WP Rocket, Perfmatters with a compatible cache plugin, or your host's built-in cache (Kinsta, WP Engine, Flywheel all include one). Enable cache preloading so pages are warm before a visitor requests them.
2. Enable Redis object cache. Redis Object Cache plugin (2.5.x) is the standard. Confirm it's connected: the plugin's diagnostics screen shows hit/miss ratio. A miss ratio above 40% on a warm cache suggests your Redis instance is undersized or your object cache keys are being evicted.
3. Upgrade PHP. Move to the highest stable version your plugins support. Check compatibility with the Plugin Health Check plugin before switching production. PHP 8.2 and 8.3 both show measurable TTFB improvements over 8.1 on WordPress 6.5+.
4. Audit slow queries. Install Query Monitor (4.17+) on a staging copy and load your highest-traffic pages. Any query taking over 50 ms is worth investigating. A single unindexed meta query can add 300–500 ms to every uncached request.
5. Then measure again.
Re-run the curl TTFB test and re-analyze access logs after each change. Only add workers if queuing persists after steps 1–4.
Before → After: The Test Site Summary
To close the loop on the WooCommerce store used throughout this tutorial:
| Metric | Before | After |
|---|---|---|
| Uncached TTFB (avg) | 1,140 ms | 610 ms |
| Cache hit rate | 0% | 88% |
| Peak uncached req/s | 3.1 | 0.37 |
| 504 errors (30-min, 50 VU) | 47 | 0 |
| PHP workers used | 2 | 2 |
| Plan cost change | — | $0 |
The changes made: Redis object cache enabled, WP Rocket installed with preloading, PHP upgraded from 8.1 to 8.2. Worker count stayed at 2. The plan was not upgraded.
Do This First
If you're seeing 504 errors or slow TTFB under traffic, run the curl uncached TTFB test and pull your access logs before you open the hosting upgrade page. In most cases, the worker count is not the binding constraint — the absence of a warm full-page cache is.
Measure your uncached request rate, measure your worker hold time, and calculate whether your current worker count is mathematically insufficient. If it is, upgrade. If it isn't, fix the cache layer first and re-measure. The numbers will tell you which lever to pull.