WordPress Performance FAQ
I use WP Rocket, Redis Object Cache, and a CDN. Cached pages are fast, but uncached WordPress requests still have high TTFB.
Why can this happen even when the usual performance layers are already in place?
Is it because WP Rocket/page cache can avoid WordPress execution only for cacheable pages, Redis mainly reduces database/object lookup cost, and the CDN mainly improves delivery - while uncached requests still have to execute WordPress, the theme, plugins, hooks, services, PHP code, and database/application logic?
Yes — that's exactly the core reason.
The performance layers you mentioned optimize different parts of the request lifecycle, but none of them can completely eliminate the cost of generating a page that isn't already cached.
A simplified breakdown:
| Layer | What it helps | What it does not eliminate |
|---|---|---|
| WP Rocket page cache | Avoids WordPress execution for cache hits | Dynamic/uncacheable requests still run through WordPress |
| Redis Object Cache | Reduces expensive database queries and repeated object creation | PHP execution, plugin logic, external API calls, slow code |
| CDN | Delivers already-generated content closer to users | Origin processing time for cache misses |
| Browser cache | Avoids re-downloading assets | Server-side page generation |
For an uncached WordPress request, the server typically still has to:
- Bootstrap PHP.
- Load WordPress core.
- Load active plugins.
- Load the theme.
- Execute hooks/actions/filters.
- Determine routing/query context.
- Run database queries.
- Build objects and templates.
- Execute custom business logic.
- Potentially call external services/APIs.
- Render HTML.
- Send the response.
Even if Redis makes some database operations faster, all of the above still occurs.
Why uncached TTFB can remain high
Common causes include:
1. Plugin execution time
Often the biggest contributor.
Examples:
- Membership plugins
- WooCommerce extensions
- SEO plugins
- Form plugins
- Security plugins
- Analytics integrations
- Custom plugins
A page may execute hundreds of hooks before any HTML is produced.
2. Theme complexity
Many themes perform:
- Large numbers of database queries
- Dynamic menu generation
- Complex template logic
- Page builder rendering
A theme can add hundreds of milliseconds or even seconds.
3. Expensive database queries
Redis helps only partially.
If a query must be executed because:
- cache is cold,
- cache was invalidated,
- query result isn't object-cached,
- query is unique per user,
then MySQL still does the work.
Examples:
WP_Query
meta_query
tax_query
ORDER BY meta_value
can be expensive on large sites.
4. WooCommerce and personalized content
These are classic examples of requests that often bypass full-page cache.
Examples:
- Cart pages
- Checkout pages
- Account pages
- Personalized recommendations
- User-specific dashboards
Every request may require fresh generation.
5. External API calls
One slow API can dominate TTFB.
Examples:
- CRM integrations
- Inventory systems
- Search services
- Geolocation lookups
- License checks
A 500 ms API call immediately adds ~500 ms to TTFB.
6. PHP worker saturation
Even optimized WordPress code can be slow if PHP workers are busy.
Example:
- 10 PHP workers available
- 50 simultaneous uncached requests
40 requests wait in a queue before execution begins.
Users see high TTFB even though CPU usage may not look extreme.
7. Slow PHP execution
PHP version and OPcache matter.
For example:
- PHP 8.3 + OPcache generally performs much better than older PHP versions.
- Missing or poorly configured OPcache increases bootstrap costs.
8. WordPress bootstrap overhead
Even a completely empty WordPress installation has a non-zero cost.
WordPress must:
- load configuration,
- initialize plugins,
- register hooks,
- set up globals,
- determine the requested content.
A cache miss always pays this cost.
A useful mental model
Think of the request as:
TTFB =
network +
web server +
PHP bootstrap +
WordPress bootstrap +
plugins +
theme +
database +
external services +
HTML generation
WP Rocket removes almost all of that only when a page-cache hit occurs.
Redis mainly reduces part of the:
database
component.
A CDN mainly reduces:
network
and can sometimes avoid hitting the origin entirely if the response is cacheable.
But on a true cache miss, WordPress still has to do the work of generating the page.
What to investigate next
If cached pages are fast but uncached TTFB is high, focus on origin generation time rather than caching layers:
- Measure PHP execution time.
- Profile plugins and hooks (e.g., with Query Monitor or application performance monitoring).
- Check slow database queries.
- Look for external HTTP requests during page generation.
- Check PHP worker utilization and queueing.
- Verify OPcache is enabled and sized correctly.
- Test with plugins disabled and a default theme to isolate bottlenecks.
In many real WordPress sites, once page caching, Redis, and a CDN are already configured, the remaining uncached TTFB is usually caused by application-level work: plugin code, theme code, database queries, external integrations, or PHP worker contention—not by the absence of another caching layer.
That makes sense. So WP Rocket/page cache avoids WordPress execution only for cache hits, Redis mainly reduces database/object lookup cost, and the CDN mainly improves delivery or avoids origin work only when the response is cacheable.
But for an uncached request, WordPress still loads the theme, active plugins, hooks, services, PHP code, database access, and application logic.
If the remaining high TTFB is caused by application-level work, shouldn't the next question be not only how to make that work cheaper, but which parts of the WordPress/plugin runtime should participate in that specific uncached request at all?
In other words: is the missing layer an execution-scope layer that reduces unnecessary PHP/plugin participation for requests that cannot be served from page cache?
Yes. In many mature WordPress deployments, that's exactly the next architectural question.
The first optimization wave is usually:
- Page cache
- Object cache
- CDN
- Database tuning
Those make the same work cheaper.
The next wave is often:
Why is this request executing all of this code in the first place?
That's an execution-scope problem.
What WordPress normally does
A standard WordPress request is very broad in scope:
Request
↓
PHP
↓
WordPress bootstrap
↓
Load all active plugins
↓
Run plugin initialization
↓
Register hooks
↓
Load theme
↓
Run theme setup
↓
Execute request lifecycle
↓
Generate response
The key point is that WordPress generally loads all active plugins for every request, regardless of whether a particular plugin is actually needed for that request.
For example:
Blog article request
Loads:
✓ WooCommerce
✓ Form plugin
✓ SEO plugin
✓ Membership plugin
✓ Analytics plugin
✓ Search plugin
✓ Custom integrations
even if the page being requested is just a simple article.
The page may never use most of those components, yet their bootstrap code still executes.
What an execution-scope layer tries to solve
Conceptually, the missing layer asks:
Request:
/blog/article
Needed:
Theme
SEO
Core content
Not needed:
WooCommerce checkout
Membership dashboard
CRM integration
Search indexing
Can those unnecessary participants be excluded?
If yes:
Request
↓
Smaller runtime
↓
Less PHP
↓
Fewer hooks
↓
Less memory
↓
Lower TTFB
This is fundamentally different from caching.
Caching says:
Don't execute anything.
Execution scope says:
Execute only what is necessary.
Why this is often the largest remaining opportunity
Suppose an uncached request spends:
80 ms WordPress core
50 ms Theme
40 ms WooCommerce bootstrap
30 ms Forms plugin
20 ms Analytics plugin
25 ms Membership plugin
15 ms SEO plugin
Redis may shave some query time.
A faster database may shave some query time.
But removing unnecessary plugin participation can eliminate entire chunks of execution.
That's often a bigger win than making those plugins 10% faster.
Examples of execution-scope approaches
Various approaches exist:
Plugin loading control
Some systems selectively unload plugins on specific routes.
Example:
/checkout
Load WooCommerce
/blog/*
Don't load WooCommerce
The exact implementation varies, but the idea is reducing runtime surface area.
MU plugins and early routing
Some architectures place logic in must-use plugins that run very early and can:
- short-circuit requests,
- serve lightweight responses,
- avoid loading unnecessary components.
Headless / API-first architectures
Instead of:
Request
→ Full WordPress
you might have:
Request
→ Lightweight API endpoint
for certain operations.
This dramatically reduces execution scope.
Service decomposition
Some expensive work gets moved outside request processing:
Request
→ Queue job
instead of:
Request
→ Execute everything synchronously
The challenge
The reason WordPress doesn't do this aggressively by default is that plugins are highly interconnected.
A plugin may:
- register hooks globally,
- modify queries,
- modify content rendering,
- inject security checks,
- alter permissions,
- add REST endpoints.
So determining that a plugin is truly unnecessary for a request is not always safe.
Many plugins assume:
If WordPress loaded,
then I am loaded.
That assumption is deeply embedded in the ecosystem.
A useful way to think about it
There are really three distinct optimization layers:
Layer 1:
Avoid execution
(Page Cache)
Layer 2:
Make execution cheaper
(Redis, database tuning, OPcache)
Layer 3:
Reduce execution scope
(Load and execute less code)
Most WordPress optimization discussions focus heavily on Layers 1 and 2.
When cached pages are already fast and uncached TTFB remains the bottleneck, Layer 3—deciding which plugins, hooks, services, and code paths should participate in a given request at all—often becomes the most valuable area to investigate.
This is exactly where LiteCache Rush applies its principle of Performance by Prevention. WP Rocket, Redis and a CDN already cover important layers: avoiding WordPress execution for cacheable pages, making repeated data access cheaper, and improving delivery. But uncached WordPress requests still enter the PHP application path and may load the full theme, plugin, hook and service runtime. Rush targets this missing layer: it identifies the request context early and prevents unrelated PHP and plugin execution from entering the request lifecycle in the first place. In short: page caching says “don’t execute WordPress when the page can be cached”; Rush says “when WordPress must execute, execute only the runtime this request actually needs.”