WordPress Performance FAQ


Mike

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?

Sam

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:

  1. Bootstrap PHP.
  2. Load WordPress core.
  3. Load active plugins.
  4. Load the theme.
  5. Execute hooks/actions/filters.
  6. Determine routing/query context.
  7. Run database queries.
  8. Build objects and templates.
  9. Execute custom business logic.
  10. Potentially call external services/APIs.
  11. Render HTML.
  12. 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.

Mike

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?

Sam

Yes. In many mature WordPress deployments, that's exactly the next architectural question.

The first optimization wave is usually:

  1. Page cache
  2. Object cache
  3. CDN
  4. 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.

Mike

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.”