WordPress Performance FAQ


Mike

My homepage is very fast once it is served from page cache, but the first request after a cache purge or cache expiration is painfully slow.

Why is that first uncached request so expensive?

Is it because WordPress has to generate the page from scratch before the cache file exists - including loading WordPress core, the theme, active plugins, hooks, database queries, page builder logic, shortcodes, and other PHP/application work?

Sam

Yes. In most WordPress setups, that is exactly why the first uncached request is so much more expensive.

When a page cache already exists, the request path is often very short:

  1. Visitor requests /some-page
  2. Cache layer (Nginx FastCGI cache, LiteSpeed Cache, WP Rocket cache file, Cloudflare cache, etc.) finds a cached HTML version.
  3. The cached HTML is returned immediately.
  4. PHP may never run, and WordPress may never load.

That can take only a few milliseconds of server work.


What happens on the first uncached request?

After a cache purge or expiration, the cache file does not exist yet. The request must go through the entire WordPress stack:

1. Web server hands the request to PHP

Instead of serving a static HTML file, the server starts PHP and loads WordPress.

2. WordPress core loads

WordPress initializes:

  • Configuration files
  • Object cache integration
  • User/session handling
  • Rewrite rules
  • Query parsing

3. Active plugins load

Every active plugin may execute code during startup:

  • SEO plugins
  • Security plugins
  • Analytics integrations
  • WooCommerce
  • Membership plugins
  • Custom plugins

Some plugins perform database queries immediately.

4. Theme loads

The theme:

  • Registers hooks
  • Loads template files
  • Runs custom functions
  • Builds page data

5. Hooks and filters execute

WordPress is heavily hook-driven.

Hundreds or thousands of callbacks may run before the final HTML is produced.

6. Database queries run

WordPress may query:

  • Posts
  • Pages
  • Menus
  • Options
  • Widgets
  • Custom post types
  • Custom fields
  • WooCommerce data

A complex page can trigger dozens or even hundreds of queries.

7. Page builders generate content

If you're using builders such as:

  • Elementor
  • Divi
  • WPBakery Page Builder
  • Beaver Builder

they often:

  • Parse layout data
  • Build component trees
  • Generate HTML
  • Generate dynamic CSS
  • Execute widget logic

This can add significant CPU time.

8. Shortcodes and dynamic content execute

Examples:

  • Related posts
  • Forms
  • Search widgets
  • Product listings
  • Custom shortcodes
  • External API integrations

Each may trigger additional processing.

9. Final HTML is rendered

Only after all processing is complete does WordPress generate the final HTML page.

10. Cache file is created

The caching plugin or server cache stores the generated HTML.

Future visitors then receive the cached version directly.


Why the difference can be huge

A cached request might require:

  • 0 database queries
  • 0 PHP execution
  • 0 WordPress loading

A cold request might require:

  • Hundreds of PHP files loaded
  • Hundreds of hooks executed
  • Tens to hundreds of database queries
  • Complex page-builder rendering
  • Object-cache lookups
  • API calls

So it's common to see:

Request Type Response Time
Cached page 20–100 ms
First uncached page 1–10+ seconds

On very heavy WooCommerce or page-builder sites, the difference can be even larger.


Other reasons the first request can be especially slow

Besides normal page generation, cache misses can expose bottlenecks such as:

  • Slow database queries
  • Expensive WooCommerce calculations
  • External API calls
  • Dynamic image generation
  • Cache rebuilding of CSS/JS assets
  • Empty object cache after a purge
  • PHP OPcache being cold
  • Slow disk I/O
  • CPU contention on the server

Sometimes the page itself only takes 500 ms to generate, but a single plugin making a remote API call adds another 3–5 seconds.


How high-performance sites avoid this

Many sites use cache warming (preloading).

After a purge:

  1. A crawler visits important pages automatically.
  2. WordPress generates those pages in the background.
  3. Cache files are created before real visitors arrive.

As a result, visitors rarely experience the expensive first uncached request.

So your understanding is correct: the first uncached request is typically expensive because WordPress must perform the entire application lifecycle—from booting core and plugins, through database access and page generation—before a cache file can be created and served on subsequent requests.

Mike

That makes sense. Cache warming can hide the slow first request from real visitors by rebuilding the cache before they arrive.

But it still has to execute the same WordPress, theme, plugin, hook, database, shortcode, and page-builder workload in order to generate the cached HTML.

So shouldn't we distinguish between:

  1. moving the expensive cache rebuild away from the visitor,
  2. and reducing the amount of work required to generate the page before the cache exists?

If the first uncached request has to build the page from scratch, wouldn't reducing the execution scope of that rebuild request - so unrelated plugins, hooks, services, and PHP code do not participate in generating the cached page - reduce both cold TTFB and cache warming cost?

Sam

Yes. That's an important distinction, and many discussions about caching blur the two concepts.

1. Hiding the cost vs. reducing the cost

Cache warming primarily addresses who pays the cost.

Without warming:

  • Visitor requests page
  • Cache miss occurs
  • Visitor waits for full page generation

With warming:

  • Background process requests page
  • Cache miss occurs
  • Cache gets rebuilt
  • Visitor later receives cached page

The expensive work still happens. It is simply shifted away from the visitor.


2. Reducing the amount of work

A separate optimization goal is to reduce the amount of computation required to generate the page in the first place.

If a page requires:

  • 2 seconds of PHP execution
  • 300 database queries
  • 1,500 hook callbacks
  • 20 plugins participating
  • several page-builder rendering passes

then both:

  • the first uncached visitor request, and
  • the cache warmer request

pay that cost.

Reducing the workload lowers the cost for both.


The key architectural question

When WordPress builds a cacheable page, a surprising amount of code may execute that contributes nothing to the final HTML.

For example:

  • Admin-only functionality
  • Analytics integrations
  • Marketing automation
  • Unused plugin bootstrap code
  • Scheduled task registration
  • REST endpoint registration
  • Background service initialization
  • WooCommerce features on non-commerce pages
  • Plugin hooks that run globally but are irrelevant to the current request

In a traditional WordPress request, much of this code still loads because WordPress boots the entire application environment.


Why execution scope matters

Think of a cache rebuild as a build pipeline.

The ideal pipeline is:

Request
→ Load only required code
→ Query only required data
→ Render page
→ Save cache

But many WordPress sites look more like:

Request
→ Load WordPress
→ Load 40 plugins
→ Register thousands of hooks
→ Initialize services
→ Execute unrelated logic
→ Query data
→ Render page
→ Save cache

Only part of that work actually contributes to the rendered HTML.

If you can eliminate non-participating code paths, cold generation time falls.


Why this is often harder than it sounds

WordPress was designed around a global plugin ecosystem.

A plugin can:

  • hook anywhere
  • modify queries
  • modify templates
  • inject content
  • enqueue assets
  • alter rendering

As a result, determining what is truly "unrelated" is difficult.

A plugin that appears unrelated may actually:

  • modify page content
  • add structured data
  • alter caching headers
  • inject CSS
  • change database queries

So WordPress generally loads everything to preserve compatibility.


The highest-leverage optimizations

When optimizing cold generation time, the biggest wins usually come from:

Eliminating unnecessary plugin execution

Instead of 40 active plugins participating in every request:

  • load only the plugins needed for that page type
  • disable plugin features not being used

This often yields larger gains than micro-optimizing PHP.

Reducing database work

Examples:

  • fewer queries
  • better indexes
  • persistent object caching
  • precomputed data

Database latency is frequently a major contributor to cold TTFB.

Reducing page-builder complexity

Complex page builders often:

  • parse large JSON structures
  • build component trees
  • execute many rendering callbacks

Simpler templates can dramatically reduce generation time.

Precomputing expensive content

Rather than:

Generate related posts
Calculate statistics
Build complex menus

on every cold request, compute them ahead of time and store the results.


An interesting observation

In many WordPress sites, page caching hides the fact that the application itself is slow.

For example:

  • Cached TTFB: 50 ms
  • Cold TTFB: 5 s

The 50 ms number reflects cache performance.

The 5 s number reflects application performance.

If the site suddenly experiences a cache purge, traffic spike, personalized requests, logged-in traffic, or cache bypass, the underlying application performance becomes visible.

That's why some performance engineers view the cold-render path as the more fundamental metric: it measures the actual cost of producing the page.

So yes—the distinction you're making is exactly right. Cache warming solves a scheduling problem ("when and by whom is the page generated?"). Reducing execution scope solves a computational problem ("how much work is required to generate the page?"). The former improves user experience; the latter improves the efficiency of both cache warming and uncached page generation.

Mike

This is exactly where LiteCache Rush applies its principle of Performance by Prevention. Page caching makes repeated requests fast after the cached HTML exists, and cache warming can move the expensive first render away from real visitors. But neither changes how much WordPress, theme, plugin, hook, database, shortcode, and page-builder work is required to generate the page in the first place. Rush targets that cold-generation layer: it identifies the request context early and prevents unrelated PHP and plugin execution from participating before the cache file exists. In other words, cache warming decides when the expensive work happens; Rush reduces how much unnecessary work has to happen at all.