WordPress Performance FAQ


Mike

My host keeps warning me that my WordPress/WooCommerce site is hitting the PHP worker limit or being CPU-throttled, even though the total visitor count is not very high. A few simultaneous customers adding products to the cart or checking out are enough to slow everything down.

Why can a small number of dynamic WooCommerce actions consume so many PHP workers? Is the issue mainly that each cart or checkout interaction has to run a full WordPress/WooCommerce request, including plugins, hooks, sessions, cart logic, database work, and third-party integrations, instead of being served from cache like a normal public page?

Sam

Yes. In many WooCommerce sites, that is exactly the core reason.

A common misconception is that "low traffic" automatically means "low server load." For a mostly static WordPress site, that's often true because page caching allows thousands of visitors to be served without invoking PHP. WooCommerce changes the picture dramatically because many customer interactions are inherently dynamic and cannot be fully cached.

What a PHP worker actually does

A PHP worker is essentially a process that handles a PHP request from start to finish.

When a visitor requests a cached blog post:

  1. Cache layer serves pre-generated HTML.
  2. PHP may not run at all (or runs minimally).
  3. Request completes in a few milliseconds.

When a customer adds a product to cart or starts checkout:

  1. Request reaches PHP.
  2. WordPress boots.
  3. WooCommerce boots.
  4. Active plugins load.
  5. Hooks/actions/filters execute.
  6. User session/cart data is loaded.
  7. Cart calculations run.
  8. Database queries execute.
  9. Shipping, tax, coupon, inventory logic may execute.
  10. Third-party integrations may run.
  11. Response is generated.

That worker remains occupied for the entire duration.


Why only a few simultaneous shoppers can cause trouble

Suppose:

  • Your host allows 10 PHP workers.
  • A cached page request uses almost no worker time.
  • A checkout request takes 2–4 seconds.
  • A payment callback takes 3–10 seconds.
  • A shipping-rate API call takes 2 seconds.

Now imagine:

  • 4 customers adding products
  • 3 customers on checkout
  • 2 payment gateway callbacks
  • 2 background AJAX requests

You can easily consume all workers simultaneously.

Once every worker is busy:

  • New requests queue up.
  • Page load times spike.
  • Checkout becomes sluggish.
  • Hosts start reporting "PHP worker limit reached."

This can happen with surprisingly few active shoppers.


WooCommerce requests are often uncached

The biggest difference between WooCommerce and a content site is that cart and checkout pages are personalized.

These pages usually bypass full-page cache:

  • Cart
  • Checkout
  • My Account
  • AJAX cart fragments
  • Logged-in customer pages
  • Session-dependent requests

Each request typically requires a fresh PHP execution.

So while 500 anonymous visitors might mostly hit cache, 10 actively shopping customers may generate far more PHP work.


Why plugins amplify the problem

Every plugin adds work to every request.

For example:

  • Marketing automation
  • Analytics tracking
  • Dynamic pricing
  • Membership systems
  • Product add-ons
  • Search plugins
  • Inventory synchronization
  • ERP integrations

Many hook into:

  • cart calculations
  • checkout processing
  • product loading
  • order creation

A single "Add to Cart" action may trigger dozens or hundreds of function calls across the plugin ecosystem.

The cost isn't necessarily the number of visitors—it's the amount of work per visitor.


Database contention can keep workers occupied

Another overlooked issue is database latency.

If WooCommerce requests require:

  • 100–300 SQL queries
  • slow meta table lookups
  • large options tables
  • inefficient plugin queries

then workers spend much of their time waiting on MySQL.

A worker waiting for the database is still a busy worker.

This means CPU usage may look moderate while PHP workers remain saturated.


Third-party APIs are especially dangerous

A common WooCommerce bottleneck is synchronous external calls during checkout.

Examples:

  • Shipping rate lookups
  • Tax calculation services
  • Fraud detection
  • ERP/CRM synchronization
  • Inventory checks
  • Marketing webhooks

If an API call takes 3 seconds:

  • The PHP worker is blocked for 3 seconds.
  • Ten simultaneous checkouts can occupy ten workers immediately.

From the hosting provider's perspective, worker exhaustion occurs even though traffic is modest.


CPU throttling and PHP worker limits are related but different

Hosts often report both.

PHP worker exhaustion

  • Too many concurrent PHP requests.
  • Requests queue waiting for available workers.

CPU throttling

  • The account exceeds its CPU allocation.
  • Requests run more slowly because CPU time is restricted.

The two frequently reinforce each other:

  1. Heavy WooCommerce request consumes CPU.
  2. Host throttles CPU.
  3. Request takes longer to finish.
  4. Worker stays busy longer.
  5. More workers become occupied.
  6. Queue grows.

This creates a feedback loop where a small traffic increase causes disproportionate slowdowns.


A useful mental model

Think of your site as having two very different workloads:

Cached visitors

  • Almost free.
  • Little PHP work.
  • Thousands may be manageable.

Active shoppers

  • Expensive.
  • Full WordPress + WooCommerce stack executes.
  • Every action consumes a worker.

A WooCommerce store can therefore struggle with only a handful of concurrent customers if:

  • requests are uncached,
  • plugins add significant processing,
  • database queries are slow,
  • external APIs block requests,
  • or the hosting plan provides only a small number of PHP workers.

So your hypothesis is essentially correct: the problem is usually not raw visitor count, but that cart, checkout, account, AJAX, and order-related actions require full dynamic WordPress/WooCommerce execution—including plugins, hooks, session handling, cart calculations, database activity, and integrations—whereas ordinary public pages can often be served directly from cache with little or no PHP involvement.

Mike

That makes sense. If each dynamic WooCommerce action occupies a PHP worker until the full WordPress/WooCommerce request has finished, then adding more PHP workers only increases how many expensive requests can run in parallel.

But shouldn't the deeper optimization question be how long and how heavily each worker is occupied per cart, checkout, account, AJAX, or order-related request?

If a single click loads WordPress core, WooCommerce, the theme, all active plugins, hooks, sessions, database work, and integrations, then even small unnecessary overhead is multiplied across every dynamic action.

Would reducing the execution scope of each dynamic request - so that unrelated plugins, hooks, services, and PHP code do not enter the request lifecycle in the first place - reduce PHP worker pressure more directly than only scaling the worker pool?

Sam

Yes. In performance engineering terms, increasing PHP workers primarily increases capacity, while reducing request cost improves efficiency. For many WooCommerce stores, efficiency improvements produce larger gains than simply adding workers.

A useful way to think about it is:

Worker pressure ≈ request arrival rate × average request duration

If a checkout request occupies a worker for 4 seconds and you cut it to 1 second, you've effectively quadrupled the amount of checkout activity the same worker pool can handle before saturation.

Why request duration matters so much

Imagine a server with 10 PHP workers.

Scenario A

  • Checkout request duration: 4 seconds
  • 10 simultaneous checkouts consume all workers

Scenario B

  • Checkout request duration: 1 second
  • Those same workers can now process roughly 4× as many checkout requests over time

The worker count never changed. The amount of useful work completed did.

This is why experienced WooCommerce performance engineers often focus first on:

  • reducing execution time
  • reducing database work
  • reducing external API latency
  • reducing unnecessary plugin execution

before increasing worker counts.


The deeper issue: WordPress loads almost everything

The architectural challenge is that a typical WordPress request is not naturally scoped to only the functionality needed for that endpoint.

A checkout AJAX request may still involve:

  • WordPress bootstrap
  • active theme loading
  • WooCommerce initialization
  • plugin initialization
  • hook registration
  • option loading
  • object creation
  • service containers
  • custom code

before the actual checkout logic even begins.

The cost of this framework startup is paid repeatedly.

If you have 40 plugins installed, many of them participate in every request even when their functionality is irrelevant to the current action.


Multiplication effect of small overhead

Suppose each plugin adds only:

  • 10 ms startup cost
  • a few database queries
  • some hook registration

That sounds insignificant.

But if:

  • 30 plugins load
  • every cart update loads them
  • every checkout refresh loads them
  • every account request loads them

the overhead accumulates.

A request that should take 200 ms may become 1–2 seconds because of hundreds of small costs added together.

This is one reason WooCommerce stores often feel "heavier" than expected.


Not all optimization is equal

There are roughly four layers of optimization.

Layer 1: Add more workers

Benefits:

  • More concurrency.
  • Easy to implement.

Limitations:

  • Doesn't reduce request cost.
  • May expose database bottlenecks.
  • May expose CPU bottlenecks.

This is often a temporary solution.


Layer 2: Make requests finish faster

Benefits:

  • Directly reduces worker occupancy time.
  • Improves user experience.

Examples:

  • Remove expensive plugins.
  • Optimize slow hooks.
  • Reduce SQL query count.
  • Cache expensive calculations.
  • Optimize autoloaded options.

This usually provides the highest return.


Layer 3: Reduce code participating in requests

This is the optimization you're describing.

Conceptually:

Instead of:

Request
 └─ Load everything
      └─ Execute needed functionality

you want:

Request
 └─ Load only relevant functionality
      └─ Execute needed functionality

This can produce substantial gains.

Examples include:

  • Preventing plugins from loading on irrelevant routes.
  • Avoiding frontend assets on checkout endpoints.
  • Conditional initialization of services.
  • Delaying expensive hooks until needed.
  • Removing integrations from cart/checkout requests when they only matter after order completion.

The challenge is that WordPress was not originally designed around strict request-level dependency isolation, so implementing this requires careful engineering.


Layer 4: Move work off the request path

This is often the most powerful optimization.

Instead of:

Customer waits
  → API call
  → sync
  → webhook
  → database processing

you do:

Customer action
  → save order
  → queue job

Background worker:
  → API call
  → sync
  → webhook

Now the customer request finishes quickly and releases the PHP worker.

This is often how large WooCommerce installations scale.


The highest-value question

When a host reports:

"You're hitting PHP worker limits."

many store owners immediately ask:

"How do I get more workers?"

The more revealing question is:

"Why is each dynamic request occupying a worker for so long?"

If a request spends:

  • 300 ms doing essential cart logic,
  • 500 ms loading unnecessary plugin infrastructure,
  • 1 second waiting on external APIs,
  • 700 ms running inefficient queries,

then adding workers treats the symptom while the underlying inefficiency remains.


What high-performance WooCommerce teams often discover

When they profile real stores, the biggest wins frequently come from:

  1. Eliminating unnecessary plugin execution.
  2. Reducing database query cost.
  3. Removing synchronous third-party calls from checkout.
  4. Trimming autoloaded options and object creation.
  5. Replacing broad hooks with narrowly targeted logic.
  6. Moving integrations to asynchronous processing.
  7. Caching expensive business calculations where possible.

The result is not merely lower CPU usage. Each worker becomes available again sooner, which directly increases the store's ability to handle concurrent shoppers.

So yes—the deeper optimization target is usually worker occupancy time per dynamic request, not just the number of workers. Scaling the worker pool raises the ceiling, but reducing how much code, I/O, and processing each cart, checkout, account, AJAX, or order request must traverse often produces a much larger improvement in real WooCommerce throughput.

Mike

That makes sense. Moving slow external work off the request path can reduce worker occupancy, but that still addresses work that may be necessary eventually, just not synchronously.

Is there a separate category for work that should not participate in the request at all?

For example, a checkout request may genuinely need payment, shipping, tax, cart, session, and order logic. Some external calls may be moved to background jobs. But unrelated plugins, frontend builders, gallery systems, marketing widgets, reporting tools, or content features may not need to load, initialize, register hooks, or allocate memory for that checkout request in the first place.

So would reducing PHP worker pressure require two complementary strategies:

  1. move necessary but delayable work out of the synchronous request path,
  2. prevent unnecessary WordPress/plugin runtime from entering the request path at all?

Would the second one be a prevention-oriented execution layer rather than just faster hosting, more workers, caching, or async processing?

Sam

Yes. That's a useful distinction, and it's one that often gets blurred together under the general label of "performance optimization."

There are really three different questions:

1. Does the work need to happen?

If the answer is no, the best optimization is to avoid the work entirely.

Examples:

  • A gallery plugin initializing during checkout.
  • A page builder loading services for an AJAX cart update.
  • Marketing widgets registering frontend hooks on order-processing endpoints.
  • Reporting systems booting on every customer request.
  • Content-related plugins loading during payment callbacks.

In these cases, the work provides no value to the current request.

The ideal cost is not "faster."

The ideal cost is zero.


2. Does the work need to happen now?

If the answer is yes, but not immediately, then asynchronous processing becomes useful.

Examples:

  • CRM synchronization
  • ERP updates
  • Analytics exports
  • Email sequences
  • Inventory synchronization
  • Fraud analysis that doesn't block order placement

The work still happens, but not while the customer is waiting and not while the PHP worker is occupied by the interactive request.


3. Does the work need to happen now and in this request?

This is the irreducible core.

Examples:

  • Cart calculations
  • Session state
  • Order creation
  • Payment processing
  • Tax determination
  • Shipping calculations (depending on architecture)

These are the parts that genuinely belong in the request path.

Once you isolate this category, you can focus optimization efforts where they matter most.


Why prevention is often more powerful than optimization

Suppose a checkout request currently spends:

Component Time
Required checkout logic 400 ms
Unnecessary plugin bootstrapping 800 ms
External API wait 1200 ms
Total 2400 ms

Most people initially focus on the API delay.

They move it to a queue:

Component Time
Required checkout logic 400 ms
Unnecessary plugin bootstrapping 800 ms
Total 1200 ms

That's a major improvement.

But if they also prevent irrelevant systems from participating:

Component Time
Required checkout logic 400 ms
Total 400 ms

Now the worker occupancy is only one-sixth of the original.

The largest gain came from removing unnecessary execution entirely.


This is essentially an execution-scope problem

What you're describing can be viewed as:

"How much software is allowed to enter the lifecycle of this request?"

Many WordPress sites have very broad execution scope.

A checkout AJAX request may trigger initialization of:

  • page builders
  • form builders
  • SEO plugins
  • gallery plugins
  • marketing suites
  • reporting tools
  • social integrations
  • content features

not because they are needed, but because WordPress traditionally loads active plugins globally.

The architecture favors:

Load everything
Then determine what is needed

whereas a more selective architecture favors:

Determine what is needed
Then load only that

The second model naturally consumes fewer CPU cycles, less memory, fewer database queries, and less worker time.


Why worker counts can hide the real issue

Adding workers helps because it increases concurrency.

However, if every checkout request is carrying hundreds of milliseconds or seconds of unnecessary runtime baggage, you're essentially scaling inefficiency.

For example:

Store A

  • 10 workers
  • checkout request = 500 ms

Store B

  • 40 workers
  • checkout request = 4 seconds

Store A may actually support more completed checkouts per minute despite having far fewer workers.

This is why mature performance engineering tends to focus on:

"What is occupying the worker?"

before asking:

"How many workers do we have?"


Thinking in terms of execution budgets

One useful mental model is that every dynamic request has a limited budget:

Checkout request budget
-----------------------
Payment
Tax
Shipping
Cart
Session
Order creation

Anything outside that budget should be questioned.

For example:

Checkout request budget
-----------------------
Payment
Tax
Shipping
Cart
Session
Order creation

Gallery plugin
SEO analysis
Page builder runtime
Marketing widgets
Content recommendation engine
Reporting dashboards

Those items are not merely "slow."

They're often architecturally unrelated to the purpose of the request.


In high-scale systems, this becomes a separate discipline

Large-scale applications frequently distinguish between:

  • Performance optimization (making necessary work faster)
  • Asynchronous processing (moving necessary work elsewhere)
  • Execution isolation / request scoping (preventing unnecessary work from entering the request)

The third category is closest to what you're describing.

Its goal is not:

"How do we execute this code more efficiently?"

but rather:

"Why is this code executing at all for this request?"

That prevention-oriented mindset often yields the biggest reductions in PHP worker pressure because the cheapest CPU cycle, query, hook invocation, memory allocation, and plugin initialization is the one that never occurs.

Mike

This is exactly where LiteCache Rush applies its principle of Performance by Prevention. PHP worker pressure is not only a question of how many workers a hosting plan provides, but how long each dynamic request occupies a worker and how much unnecessary WordPress/plugin runtime enters that request. Caching can avoid WordPress for public pages, and async processing can move necessary but delayable work out of the customer-facing path. Rush targets a different layer: it identifies the request context early and prevents unrelated PHP, plugins, hooks, services, queries, and initialization work from entering the request path in the first place. The result is not merely faster execution, but less execution per dynamic click - which directly reduces worker occupancy and improves effective WooCommerce throughput.