WordPress Performance FAQ
I run a high-traffic WordPress affiliate site with comparison tables, dynamic pricing widgets, product boxes, tracking links, and external price/API integrations. Most public pages are served from full-page cache and are fast.
But some requests bypass the cache or trigger dynamic generation, and those requests consume huge CPU and sometimes kill the server.
Why does this happen even though the site is mostly cached? Is the issue that cache-bypassing requests still have to execute WordPress, the theme, affiliate plugins, comparison-table logic, pricing widgets, hooks, database queries, and external API integrations instead of being served as static HTML?
Yes. In many high-traffic WordPress affiliate sites, that is exactly what happens.
A full-page cache only helps for requests that can be satisfied directly from the cached HTML. The moment a request bypasses the cache, WordPress has to generate the page dynamically, which can be surprisingly expensive.
A cache hit might look like:
Visitor
↓
Web server
↓
Cached HTML file
↓
Response
Very little PHP, database activity, or plugin logic runs.
A cache miss or cache bypass often looks like:
Visitor
↓
Web server
↓
PHP
↓
WordPress bootstrap
↓
Theme
↓
Plugins
↓
Hooks/filters
↓
Database queries
↓
Affiliate logic
↓
Price/API integrations
↓
HTML generation
↓
Response
That entire stack executes for every uncached request.
Why a small percentage of uncached requests can cause huge CPU usage
Suppose:
- 99% of requests are cache hits
- 1% bypass cache
- Cached request costs ~1 ms of CPU
- Dynamic request costs ~500 ms of CPU
For 100,000 requests:
99,000 cached requests
= 99 seconds CPU
1,000 dynamic requests
= 500 seconds CPU
The 1% of requests consume most of the CPU.
This is why servers can become overloaded even when cache hit rates look excellent.
What makes affiliate sites especially expensive
Many affiliate sites have components that are significantly heavier than a typical blog:
Comparison tables
Large comparison tables often require:
- Custom post type queries
- Metadata lookups
- Sorting/filtering logic
- Product attribute calculations
These can generate many database queries and PHP processing steps.
Dynamic pricing widgets
If pricing is updated dynamically:
- Database lookups occur
- Pricing calculations may run
- External API calls may be triggered
- Widget rendering logic executes
Even if the final output is small, generating it can be costly.
Product boxes
Many product-box plugins:
- Query multiple custom fields
- Generate tracking links
- Compute ratings or scores
- Render complex templates
Repeated dozens of times on a page, costs add up.
Affiliate link tracking
Some tracking systems:
- Log clicks
- Update statistics
- Resolve redirects
- Fire hooks and analytics code
High click volume can create substantial load.
External APIs
These are often the biggest problem.
If an uncached request triggers:
- Amazon-related data retrieval
- Price feeds
- Merchant APIs
- Availability checks
Then PHP workers can spend time waiting on remote services.
A request that waits 2 seconds on an API still occupies a PHP worker for those 2 seconds.
Why PHP workers get exhausted
The real bottleneck is often not raw CPU but PHP worker saturation.
Imagine:
- 20 PHP workers available
- Dynamic requests take 3 seconds
Then:
20 workers × 3 seconds
Only a limited number of dynamic requests can be processed simultaneously.
Once all workers are busy:
- New requests queue
- Response times increase
- Timeouts occur
- CPU may spike
- Server can appear to "die"
Meanwhile, cached requests may still be fast because they never reach PHP.
Common cache-bypassing requests
Examples include:
- Logged-in users
- Admin-ajax.php requests
- REST API endpoints
- Search pages
- Filtered/sorted comparison pages
- Query-string URLs
- Tracking URLs
- Personalized content
- Session-based content
- Pages excluded from cache
- Cache misses after expiration
Many site owners focus on page views but discover that a much smaller volume of these dynamic requests is consuming most server resources.
Why WordPress itself contributes
A dynamic request does not load only the requested page.
WordPress typically:
- Loads core files.
- Loads all active plugins.
- Loads the theme.
- Registers hooks.
- Executes plugin callbacks.
- Performs database queries.
- Builds the page.
- Sends the response.
If you have dozens of plugins, each uncached request may execute a significant amount of PHP before your page-specific logic even starts.
The key insight
For a heavily cached affiliate site, server load is often determined not by total traffic but by:
(number of uncached requests)
×
(cost per uncached request)
So yes—the underlying issue is usually that cache-bypassing requests must execute the full WordPress stack (core, theme, plugins, hooks, database work, comparison tables, pricing widgets, tracking logic, and potentially external API integrations) instead of being served as pre-generated static HTML. A relatively small number of these expensive dynamic requests can consume the majority of CPU, memory, PHP workers, and database resources on the server.
That makes sense. So the real load problem is not total traffic alone, but:
uncached request volume × cost per uncached request.
But shouldn't we also ask what makes each uncached affiliate request so expensive?
For example, a comparison-table or pricing-widget request may genuinely need product data, affiliate-link logic, cached price data, tracking rules, and maybe some external feed/API integration. But it probably does not need unrelated form builders, popup systems, WooCommerce checkout logic, page-builder modules, admin/reporting tools, import/export systems, or other plugins that do not contribute to generating that specific response.
So if these cache-bypassing requests cannot always be avoided, would the deeper optimization layer be reducing their execution scope - identifying the request type early and preventing unrelated plugins, hooks, services, widgets, and PHP code from entering the request lifecycle in the first place?
Yes. In many mature WordPress systems, that's where the biggest gains come from after you've already achieved a high cache-hit ratio.
Your formula:
Load = uncached request volume × cost per uncached request
can be expanded further:
Load = uncached request volume × (bootstrap cost + plugin cost + query cost + external service cost + rendering cost)
Most site owners focus on reducing uncached request volume. That's important, but eventually you reach a point where some requests must remain dynamic. Then the next question becomes:
"Why is this request executing hundreds of thousands of lines of PHP that have nothing to do with the response?"
And that's often where affiliate sites lose enormous amounts of performance.
The hidden problem: WordPress loads almost everything
A standard WordPress request doesn't load only the code needed for that request.
Typically:
Request
↓
WordPress bootstrap
↓
Load active plugins
↓
Plugin initialization
↓
Hook registration
↓
Theme initialization
↓
Widget registration
↓
Shortcode registration
↓
Request-specific work
Even if the request only needs:
comparison table
cached product data
affiliate links
the server may also initialize:
form builder
popup system
WooCommerce
email automation
analytics plugins
admin reporting
page-builder modules
import/export tools
A/B testing
social sharing
SEO modules
before it ever reaches the code that matters.
A useful way to think about it
Imagine an uncached comparison-table request needs:
Actual business logic:
50 ms
But WordPress spends:
Plugin A init: 20 ms
Plugin B init: 35 ms
Plugin C init: 15 ms
Plugin D init: 50 ms
Theme startup: 40 ms
Unused hooks: 60 ms
before the comparison table logic even runs.
Now the request costs:
50 ms useful work
220 ms framework/plugin overhead
------------------------------
270 ms total
The majority of execution time isn't generating the page.
It's preparing code paths that are never used.
This is especially common on affiliate sites
Affiliate sites often evolve over years.
They accumulate:
- comparison plugins
- table plugins
- schema plugins
- tracking plugins
- page builders
- lead-capture systems
- email integrations
- analytics
- SEO tools
- redirect managers
- WooCommerce remnants
- import systems
Each plugin may seem harmless individually.
But on every uncached request:
Plugin startup cost
+
hook registration
+
autoloaded options
+
dependency loading
+
database lookups
gets paid again.
Early request classification is a powerful optimization
The architectural idea is:
What kind of request is this?
Determine that as early as possible.
Examples:
Product comparison page
Needs:
comparison engine
product data
affiliate links
pricing cache
Does not need:
contact forms
newsletter popups
WooCommerce checkout
admin reports
import/export
Tracking redirect
Needs:
link resolution
click logging
redirect
Does not need:
theme rendering
page builder
widgets
SEO rendering
comparison tables
Pricing API endpoint
Needs:
product cache
pricing service
response serializer
Does not need:
frontend theme
marketing plugins
popup systems
The earlier you can identify the request, the more work you can avoid.
This is essentially reducing the request surface area
Think of each request having a "working set."
Bad architecture:
Every request
→ Load everything
→ Use 5%
Better architecture:
Request type A
→ Load only A components
Request type B
→ Load only B components
Request type C
→ Load only C components
Large web applications outside WordPress often do this aggressively.
Why plugin count is a misleading metric
People often say:
"I only have 25 plugins."
The number itself doesn't matter much.
What matters is:
How much code executes
on every uncached request?
A single poorly designed plugin can consume more CPU than twenty lightweight plugins combined.
The expensive part is usually:
- plugin initialization
- autoloaded options
- hook callbacks
- database queries
- remote API calls
not the plugin count itself.
External APIs are a special case
Even if you eliminate unnecessary plugins, a request can still be expensive if it waits on remote systems.
Example:
WordPress work: 80 ms
Database work: 30 ms
Remote pricing API: 1.5 s
Total:
1.61 s
The PHP worker remains occupied for the entire wait.
For affiliate sites, a common optimization is:
Background refresh
↓
Store locally
↓
Serve cached data
instead of:
Visitor request
↓
Call remote API
↓
Wait
↓
Render page
Where the biggest wins often come from
Once caching is already good, the highest-leverage improvements are often:
- Eliminate unnecessary code from uncached requests.
- Prevent irrelevant plugins/services from initializing.
- Reduce hook execution paths.
- Reduce database queries and object creation.
- Move API calls out of the request path.
- Use precomputed product/comparison data where possible.
- Create lightweight request handlers for tracking, redirects, and data endpoints.
At a certain scale, the question shifts from:
"How do I cache more pages?"
to:
"For the requests that must remain dynamic, how can I ensure only the minimum required code executes?"
That's often the next major performance frontier for high-traffic affiliate WordPress sites.
That makes sense. Moving live price/API calls out of the visitor request path and serving precomputed or cached affiliate data can reduce a major source of latency.
But even after pricing data is cached locally, a cache-bypassing comparison-table, tracking, search, or pricing-widget request may still bootstrap WordPress, the theme, active plugins, hooks, widgets, shortcodes, and services before it generates the response.
So shouldn't we distinguish between two separate optimizations?
- Avoid live external/API work during the request by precomputing or caching affiliate data.
- Reduce the WordPress/plugin runtime that participates in the request at all.
In other words, for dynamic affiliate requests that still need WordPress context, would the next layer be early request classification and known-safe execution profiles, so only the plugins and PHP subsystems required for that specific comparison, pricing, tracking, or content request are loaded, while unrelated runtime falls back to full stack only when uncertainty exists?
Yes. Those are two different optimization layers, and high-scale WordPress systems often need both.
A useful mental model is:
Dynamic Request Cost
=
Data Acquisition Cost
+
Application Runtime Cost
Where:
Data Acquisition Cost
=
API calls
feed retrieval
remote lookups
and:
Application Runtime Cost
=
WordPress bootstrap
plugin initialization
hook execution
theme loading
database work
rendering
Many affiliate sites successfully optimize the first term but never seriously address the second.
Layer 1: Eliminate live data acquisition
This is the optimization most people think about first.
Instead of:
Visitor
↓
Request
↓
Affiliate API
↓
Wait
↓
Render
you do:
Background job
↓
Affiliate API
↓
Local cache/database
Visitor
↓
Request
↓
Read local data
↓
Render
This removes:
- network latency
- API failures
- rate-limit issues
- long worker occupancy
For many affiliate sites this is a huge win.
But after doing this, you often discover something surprising:
The request is still expensive.
Because you've removed the remote dependency but not the application overhead.
Layer 2: Reduce runtime participation
This is the deeper architectural problem.
Suppose a pricing widget request now reads from local cache in 5 ms.
The request may still spend:
40 ms WordPress bootstrap
60 ms plugin initialization
30 ms hook registration
25 ms widget setup
20 ms shortcode setup
15 ms theme logic
5 ms actual pricing lookup
Total:
195 ms
The business logic is only a tiny fraction of the request.
Early request classification becomes important
At this stage the question changes from:
"How do we make the work faster?"
to:
"How do we avoid doing unnecessary work at all?"
You classify requests as early as possible.
For example:
Tracking redirect
Comparison table
Pricing widget
Search endpoint
Product page
Admin request
Each class has different requirements.
Known-safe execution profiles
This is very close to how large application platforms think about performance.
Conceptually:
Tracking Request
needs:
link resolution
logging
redirect
and does not need:
theme
page builder
WooCommerce checkout
form systems
popup systems
comparison rendering
While:
Comparison Table Request
needs:
product data
comparison engine
affiliate logic
but may not need:
newsletter integrations
admin reporting
import/export systems
checkout flows
So you end up with something like:
Request Type
↓
Execution Profile
↓
Allowed Components
↓
Response
rather than:
Request Type
↓
Load Everything
↓
Response
Why this can produce larger gains than query optimization
Imagine a request currently costs:
Database queries: 30 ms
Business logic: 40 ms
Plugin overhead: 250 ms
Theme overhead: 60 ms
Total:
380 ms
You spend weeks optimizing queries and reduce them to:
15 ms
Now:
365 ms
Only a small improvement.
But if you eliminate most unrelated runtime:
Database queries: 15 ms
Business logic: 40 ms
Plugin overhead: 40 ms
Theme overhead: 10 ms
Total:
105 ms
A much larger gain.
The challenge is safety
The reason WordPress loads broadly is that dependency relationships are often implicit.
A plugin may:
- register hooks globally
- alter queries
- modify post content
- inject filters
- add shortcodes
- modify redirects
If you disable it for a request, something unexpected may break.
That's why the practical approach is usually:
Conservative mode
Unknown request
→ Full stack
Known profile
Tracking redirect
→ Minimal stack
Pricing endpoint
→ Minimal stack
Comparison endpoint
→ Reduced stack
Only requests whose dependencies are well understood receive the optimized path.
This is essentially moving from "cache-first" to "execution-aware" architecture
Many WordPress performance discussions stop at:
Increase cache hit ratio
But at scale the next frontier is:
What executes when cache is missed?
And after that:
What absolutely must execute?
Those are different questions.
A mature affiliate platform often ends up with three layers:
- Serve static content whenever possible.
- Precompute expensive affiliate/pricing data outside visitor requests.
- For unavoidable dynamic requests, execute the smallest verified subset of the application required to satisfy that request.
That third layer is where concepts such as request classification, execution profiles, selective plugin participation, lightweight endpoints, service boundaries, and dependency-aware loading become valuable. Once cache misses become the dominant source of resource consumption, reducing the runtime footprint of those misses is often the highest-leverage optimization remaining.
This is exactly where LiteCache Rush applies its principle of Performance by Prevention. A high-traffic affiliate site can already have full-page caching, cached pricing data, background feed updates, and local product data - yet cache-bypassing requests may still become expensive because they bootstrap the full WordPress, theme, plugin, hook, widget, shortcode, and service runtime. Rush targets that remaining layer: it identifies the request context early and applies a known-safe execution profile before the full plugin stack is loaded. For comparison tables, pricing widgets, tracking redirects, search endpoints, and other dynamic affiliate requests, the goal is not only to cache more data, but to prevent unrelated PHP and plugin execution from participating in the request in the first place.