Web Hosting Cache Explained: The 4 Layers and What Each One Does

Mangesh Supe, Hosting Performance Analyst

By

Founder, ThatMy.com • Independent Hosting Benchmarks • ISP & Network Infrastructure Background

X LinkedIn How we test →

Web Hosting Cache Explained: The 4 Layers and What Each One Does

Most WordPress owners think they have caching configured because they installed one plugin. They have configured one layer of a six-layer system. The other five layers are either running at default settings they have never touched, not configured at all, or actively working against each other. This guide covers all six layers, explains the failure mode of each, and gives you a complete picture of what caching actually does in a WordPress hosting environment.

The fastest WordPress sites are not running better themes. They are running complete cache stacks. A well-optimized WordPress install on a modest VPS serves pages in under 100ms. An unoptimized install on a powerful dedicated server takes two seconds. The hardware is not the variable. The server resources help, but caching is the multiplier. This guide starts with the model, then covers every practical implementation decision you need to make.

What Cache Fixes

High TTFB from PHP execution. Repeated database queries. Server overload under traffic spikes. Slow page loads for every visitor.

What Cache Does NOT Fix

Slow JavaScript execution. Unoptimized images. Render-blocking scripts. Bad hosting hardware. Application-layer bugs.

Fastest Free Setup

LiteSpeed Cache on LiteSpeed host, or WP Super Cache on any host. Add Cloudflare free tier for CDN cache on top.

The Six-Layer Cache Model Every WordPress Site Has (Whether You Know It or Not)

Every WordPress hosting setup already has all six cache layers — but most of them are running in their worst possible state. Your browser has a cache. Your CDN has a cache. Your server has a page cache, an object cache, an OPcache, and a database cache. Install WordPress fresh with no plugins and all six exist in some form. The question is not whether caching is happening. The question is whether it is configured to help you.

Here is what each layer does and what happens when it is not configured:

1

Browser Cache

The visitor's browser stores static files locally after the first visit: CSS, JavaScript, images, fonts. On return visits, the browser loads these from disk instead of making network requests. Default state without configuration: no Cache-Control headers, so the browser re-downloads every asset on every visit. A properly configured browser cache means returning visitors load your site almost entirely from their local disk.

Client-Side
2

CDN Cache

CDN edge nodes cache static assets and (with configuration) full HTML pages at servers near your visitors. A visitor in Germany gets assets from a Frankfurt node instead of your US server. Default state without configuration: only images and CSS are cached; HTML pages pass through to origin on every request. Cloudflare APO or a full-page cache rule changes this. The CDN guide covers the full CDN caching model in detail.

Network
3

Page Cache

The most impactful layer for WordPress. Page cache stores the fully rendered HTML output of each page as a static file or in memory. When the next visitor requests that page, the server returns the static copy directly, skipping PHP execution and all database queries. Default state without configuration: WordPress generates every page fresh from PHP and MySQL on every request. This is the single biggest performance gap on most WordPress sites.

Server-Side
4

Object Cache

Object cache stores the results of expensive database queries and computed values in RAM (via Redis or Memcached). When WordPress needs a value it has computed before, it reads from memory instead of running the query again. Default state without configuration: WordPress uses its own in-memory object cache within a single PHP request, but nothing persists between requests. Without persistent object cache (Redis), every new request re-runs every query from scratch.

Server-Side
5

PHP OPcache

OPcache stores compiled PHP bytecode in shared memory. Without it, PHP re-parses and re-compiles every file on every request. With a WordPress install that loads 150+ PHP files per page, this compilation overhead adds meaningful CPU cost. Default state without configuration: most modern hosts enable OPcache by default, but the configuration (memory allocation, validation frequency) matters as much as whether it is on.

PHP Layer
6

Database Cache (InnoDB Buffer Pool)

MySQL's InnoDB engine maintains a buffer pool in RAM: a cached copy of frequently accessed database pages. When a query hits a page already in the buffer pool, no disk read is required. Default state without configuration: the buffer pool defaults to 128 MB, which is often too small for a production WordPress install. A properly sized buffer pool (typically 50-70% of available RAM) turns most database reads from disk operations into RAM operations.

Database
WordPress caching stack: six layers from browser cache at top to database cache at bottom, with request flow arrows and cache hit bypass indicators
Cache LayerWhere It LivesWhat It StoresTypical TTLNeeds Manual Invalidation?
Browser CacheVisitor's local deviceStatic assets: CSS, JS, images, fonts24h to 1 year (max-age header)No — browser respects headers; stale assets after deploy
CDN CacheCDN edge nodes globallyStatic assets + full pages (with APO or rules)30 min to 24h typicalYes — purge via API or CDN dashboard after deploy
Page CacheYour server (disk or RAM)Fully rendered HTML outputUntil next publish, manual purge, or TTLYes — plugins handle this on WordPress content changes
Object CacheServer RAM (Redis/Memcached)DB query results, transients, computed dataConfigurable per key (seconds to hours)Automatic on write — stale keys expire or are overwritten
PHP OPcacheServer shared memoryCompiled PHP bytecode (not content)Until PHP file changes or restartAutomatic — OPcache detects file changes when configured
Database CacheMySQL InnoDB buffer pool (RAM)Frequently read database pagesLRU eviction — no explicit TTLAutomatic — MySQL manages this internally

The practical takeaway: page cache is the highest-impact layer to configure because it eliminates the two most expensive operations (PHP and database) for the most common case (public, non-personalized pages). Object cache is the highest-impact layer for sites where full page caching is not possible, like WooCommerce with active shoppers. OPcache should be verified enabled, not configured from scratch. The browser cache and database buffer pool are largely set-and-forget once you handle them once.

Cache Request Lifecycle: What Happens the Moment a Request Arrives

Every cache layer makes the same decision in microseconds: do I have a valid copy of this response, or do I need to generate it? Understanding this decision tree is what makes cache debugging tractable. When something is slow or broken, you can trace exactly which layer is failing and why.

Cache request lifecycle: HIT path returns from lookup instantly, MISS path runs through PHP execution, database query, and response generation
Request Arrives

Visitor's browser sends GET request to your domain. DNS resolves to Cloudflare (or your server directly). The request begins its journey through every cache layer in order.

Cache Lookup

The cache layer (CDN, page cache plugin, or server config) checks whether a valid cached copy of this URL exists. Key factors: is the URL cacheable? Are there session cookies that require bypass? Has the TTL expired?

CACHE HIT

A valid cached copy exists. The cache layer returns the stored response immediately. PHP does not run. MySQL does not run. Total time: 5 to 50ms depending on layer and distance from visitor.

Response delivered. Done.
CACHE MISS

No valid cached copy. The request passes through to the next layer — CDN miss goes to origin; page cache miss goes to PHP; object cache miss goes to the database.

Continue to backend generation.
Backend Generation (MISS Path)

PHP boots, loads WordPress, runs the template, executes database queries, builds the HTML response. On a busy shared host with no object cache, this can take 800ms to 2 seconds. The generated response is then stored in the cache for the next request and returned to the visitor.

The Number That Defines Performance

Your cache hit ratio is the percentage of requests served from cache without hitting the backend. A blog with no logged-in users can achieve 95%+ hit ratios. A WooCommerce store with active shoppers might see 40-60%. A membership site where most users are logged in might see 10-20%. The hit ratio determines whether your server is handling 100% of traffic or only the uncacheable fraction. At a 90% hit ratio, your origin processes one-tenth of actual traffic. That is the difference between a server that holds at 2,000 concurrent visitors and one that collapses at 200.

The Bypass Conditions That Break Hit Ratios

Cache plugins and CDNs automatically bypass cache under specific conditions. Knowing these conditions tells you exactly where your hit ratio is leaking.

  • Authenticated sessions: Any request with a wordpress_logged_in_* cookie bypasses page cache. Editors, admins, and logged-in members never get cached HTML pages.
  • WooCommerce cart cookies: Once a visitor adds to cart, the woocommerce_cart_hash cookie is set. Most cache configurations treat this as a bypass signal for all pages, not just cart/checkout.
  • Cache-Control: no-store: If your origin (a plugin, security tool, or server config) sends this header, every cache layer in the chain obeys it and stores nothing.
  • URL query strings: Some cache configurations treat ?preview=true or ?s=searchterm as uncacheable dynamic URLs, sending them to backend on every request.
  • POST requests: Page cache only covers GET requests. Form submissions, AJAX calls, and REST API requests go straight to backend.

Page Cache vs Object Cache: The Distinction That Determines Your Strategy

Ask ten WordPress developers what caching is and nine will describe page cache. The tenth will say "depends on the page." That tenth person has a WooCommerce store, and they understand why object cache exists. The distinction between these two types determines every implementation decision you make — which plugin to use, whether to pay for Redis, and what to do when full-page caching is not possible.

Page cache vs object cache: page cache stores complete HTML output, object cache stores individual database query results, with arrows showing what each type eliminates from the request pipeline

Page Cache

Stores the complete rendered HTML of a page
  • What it eliminates: All PHP execution, all database queries, all WordPress bootstrap for cached pages
  • Speed gain: TTFB drops from 200-2000ms to 10-50ms
  • Works on: Public, non-personalized pages — blog posts, static pages, product pages for logged-out visitors
  • Fails on: Cart pages, checkout, account pages, any page with user-specific content
  • Storage: Disk files or RAM (depends on plugin)
  • Typical TTL: Until next content publish or manual purge

Best for: Content sites, blogs, informational pages, product pages for unauthenticated visitors

Object Cache

Stores results of individual operations in RAM
  • What it eliminates: Repeated database queries, repeated API calls, expensive computed values
  • Speed gain: Individual query time drops from 10-200ms to under 1ms per cached object
  • Works on: Any page, including personalized, authenticated, dynamic pages
  • Fails on: Nothing — it is additive. But does not eliminate PHP overhead.
  • Storage: Redis or Memcached (RAM-based)
  • Typical TTL: Seconds to hours, per cache key

Best for: WooCommerce stores, membership sites, logged-in users, pages that cannot use page cache

The Combination That Actually Wins

The confusion comes from treating these as an either/or choice. They are complementary. A complete WordPress cache strategy uses both: page cache handles logged-out visitors (the majority of traffic for most sites), object cache handles logged-in users and dynamic pages. The page cache absorbs 80-95% of your traffic load. The object cache makes the remaining 5-20% (authenticated users, cart sessions, WooCommerce queries) significantly faster.

Here is how the combination works in practice for a WooCommerce store. A first-time visitor lands on a product page. Page cache serves the pre-rendered HTML in 30ms. That visitor adds to cart. Now WooCommerce sets a session cookie. Every subsequent page load for that visitor bypasses page cache. Object cache steps in: when WordPress looks up product availability, user metadata, or navigation menus, these values are pulled from Redis in microseconds instead of queried from MySQL. The page is still generated by PHP, but it is generated much faster because the expensive operations are cached at the object level.

Best WordPress Cache Plugins in 2026: Honest Comparison

There are six plugins worth considering. Four of them are the right choice for four very different situations. Install the wrong one and you either pay for features your server cannot use, or you leave your best performance layer unconfigured. I have worked with all six across dozens of WordPress setups. The choice is not about which is "best" — it is about which fits your server and your site type.

PluginPriceKey FeaturesAuto CDN PurgeImportant Caveat
WP Rocket$59/yr (1 site)Page cache, CDN, minify, lazy load, database cleanup, cache preloadingYes (with Cloudflare plugin)No — paid only. Best all-in-one for non-LiteSpeed hosts.
LiteSpeed CacheFreePage cache, object cache, CDN, image optimizer, critical CSSYes — built-inOnly on LiteSpeed/OpenLiteSpeed web servers. Fastest on compatible hosts.
FlyingPress$99/yr (1 site)Page cache, Redis object cache, CDN integration, above-the-fold CSSYes — built-inBest performance-per-feature ratio for modern optimized setups.
W3 Total CacheFree / $99/yr ProPage cache, object cache, DB cache, CDN, browser cache, minifyYes (with plugin)Most complex configuration. Most flexible. Easiest to misconfigure.
WP Super CacheFreePage cache only (static files or PHP mode)No — manual onlySimple and reliable. No minification, no CDN, no object cache. Good for low-complexity sites.
Cloudflare (APO)$5/mo add-onFull HTML page cache at Cloudflare edge, auto-purge on publishBuilt-in (it IS Cloudflare)Not a WordPress plugin per se — requires Cloudflare plugin. Caches pages at CDN edge, not on-server.

WP Rocket: The Paid Plugin Most Sites Should Choose

WP Rocket is not the cheapest option, but it is the one that works correctly with no configuration for most WordPress setups. The defaults are sane. The WooCommerce exclusions are pre-configured. The CDN integration with Cloudflare purges cache automatically on publish. I recommend WP Rocket for any WordPress site on an Apache or Nginx host that does not run LiteSpeed. At $59/year for a single site, it costs less than an hour of developer time spent debugging a misconfigured free plugin. One caveat: WP Rocket does not manage object cache. Pair it with the Redis Object Cache plugin separately if your host supports Redis.

LiteSpeed Cache: The Fastest Free Option (With a Critical Requirement)

LiteSpeed Cache is the single most powerful free cache plugin available, but it requires LiteSpeed web server to unlock the features that make it special. On a LiteSpeed host, it manages page cache at the server level (not PHP level), object cache via Redis, image optimization, critical CSS generation, and CDN configuration from a single plugin. On an Apache or Nginx host, most of these features are disabled. Before installing LiteSpeed Cache, check your server type in cPanel or by running curl -sI yourdomain.com | grep -i server. If the Server header shows LiteSpeed, install LiteSpeed Cache. If it shows Apache or Nginx, install WP Rocket or FlyingPress instead.

FlyingPress: The Best Performance-Per-Dollar Paid Option

FlyingPress launched in 2020 and has quietly become the most technically sophisticated paid cache plugin. Its critical CSS generation is faster than WP Rocket's. Its Redis object cache integration is built in and requires no separate plugin. Its page caching logic handles edge cases like WooCommerce exclusions more granularly than most competitors. At $99/year for a single site, it is more expensive than WP Rocket's single-site price. The price is worth it for performance-focused developers managing complex WordPress setups on non-LiteSpeed hosts who want everything in one plugin.

W3 Total Cache: Maximum Flexibility, Maximum Complexity

W3 Total Cache can do more than any other free plugin. Page cache, database cache, object cache, browser cache, minification, CDN integration, varnish integration, fragment cache — it handles every layer. The problem is that it requires you to configure each layer correctly. The default settings are often wrong. A W3 Total Cache install I audited last year had page cache enabled but the cache directory pointed to a RAM-limited tmpfs mount, CDN URLs configured but the CDN pull zone was not created, and minification enabled on a site that used a page builder that generated inline CSS, breaking the layout. The plugin has free tier and a Pro version at $99/year for commercial features like New Relic integration and fragment caching. Use it only if you are comfortable reading documentation and testing each setting.

Cloudflare APO: Not a Plugin, an Architecture Decision

Cloudflare APO is not a traditional cache plugin. It is a Cloudflare Worker that caches full WordPress HTML pages at Cloudflare's edge globally. The WordPress plugin that enables it handles cache purge signaling only. This matters because APO solves a problem no on-server cache plugin can: geographic latency. A cached page served from a Cloudflare edge node in Mumbai to an Indian visitor arrives in 15ms. The same page served from a cached file on your Texas server takes 250ms for physics alone. APO costs $5/month on the Cloudflare free plan or is included in Cloudflare Pro. For any site with significant international traffic, APO is the highest-impact cache investment per dollar available.

Object Cache Deep Dive: Redis vs Memcached for WordPress

The object cache conversation always collapses to "Redis or Memcached?" The honest answer: Redis. Not because Memcached is bad, but because Redis has one property that makes it categorically more reliable for WordPress specifically: persistence. Memcached loses everything in RAM when the server restarts. Redis does not. For a WooCommerce store, losing the object cache on every server restart means a cold-start database hammering on your busiest traffic periods. After a restart during a traffic spike, you are back to querying MySQL for every transient, every menu, every product availability check. Redis warms back up from its persistent store. Memcached starts from scratch.

Redis vs Memcached: Redis persistent storage with data structure support versus Memcached volatile in-memory key-value store, with WordPress integration arrows
FeatureRedisMemcachedVerdict
Data persistenceYes — survives server restart (RDB/AOF)No — lost on restartRedis wins for WordPress: transients survive restarts
Data structuresStrings, lists, sets, hashes, sorted setsStrings only (key-value)Redis more flexible for complex cached objects
Max value size512 MB per key1 MB per key (default)Large cached objects may exceed Memcached limit
Multi-threadingSingle-threaded (Redis 6+ has some multi-thread I/O)Multi-threadedMemcached can be faster under extreme parallelism
WordPress pluginRedis Object Cache (Tillman Bauknecht) or Object Cache ProW3 Total Cache or WP-CLI dropinRedis has better WordPress ecosystem support
Memory efficiencyHigher (metadata per key)Lower (leaner structure)Memcached uses ~30% less RAM per key
ClusteringRedis Cluster (built-in)External sharding onlyRedis scales out more cleanly
Hosting availabilityScalaHosting, Cloudways, Kinsta, WP EngineSome shared hosts (less common)Redis more widely available on managed WordPress hosts
Recommended for WordPressYes — first choiceOnly if Redis unavailableRedis is the correct choice for WordPress object cache

Setting Up Redis on WordPress

Redis availability varies by host. ScalaHosting and Cloudways both include Redis on their plans. Kinsta, WP Engine, and Pagely manage Redis automatically as part of their WordPress environment. On a VPS where you have root access, Redis installs in minutes:

Redis setup on Ubuntu/Debian VPS:
# Install Redis
sudo apt install redis-server -y

# Verify Redis is running
redis-cli ping
# Expected: PONG

# Check Redis version
redis-cli --version

# In WordPress, install the Redis Object Cache plugin:
wp plugin install redis-cache --activate

# Enable the object cache dropin:
wp redis enable

# Verify connection:
wp redis status

After enabling Redis, verify it is actually serving cache hits rather than sitting idle. The Redis Object Cache plugin's dashboard shows hit/miss ratios in real time. A healthy WordPress install with Redis should see 60-90% hit ratios on object cache within minutes of enabling it on a site with real traffic.

Object Cache Pro vs the Free Redis Plugin

The free Redis Object Cache plugin (by Tillman Bauknecht) is the standard WordPress Redis integration and works correctly for most sites. Object Cache Pro is a paid alternative ($95-$450/year depending on tier) built specifically for high-traffic WordPress and WooCommerce sites. The meaningful technical differences: Object Cache Pro uses a persistent connection per PHP worker (rather than reconnecting per request), supports Redis Cluster for high-availability setups, and has more granular per-group TTL controls. For sites under a few hundred concurrent users, the free plugin is sufficient. For WooCommerce stores handling hundreds of concurrent active shoppers, Object Cache Pro's connection handling is measurably better under load.

When Object Cache Does Not Help

Object cache is useless if page cache is serving the request. A cached HTML page bypasses PHP entirely. There is no object cache lookup because PHP never runs. Object cache is also not helpful for queries that are inherently uncacheable — queries that return different results based on user session data, real-time inventory, or time-sensitive pricing. Cache what is cacheable, and accept that some data must always go to the database. The goal is not to cache everything. It is to identify the expensive operations that run repeatedly and cache those specifically.

PHP OPcache Internals: The Cache Layer Most People Never Think About

OPcache is the only cache layer in this guide that you almost certainly did not configure and are already benefiting from — because most quality hosts enable it by default. But "enabled" and "configured correctly" are different things, and the difference is visible in TTFB.

PHP OPcache internals: source file compiles to opcodes stored in shared memory, subsequent requests read bytecode directly, bypassing file parsing and compilation

What OPcache Actually Does

PHP is an interpreted language. Without OPcache, every PHP file your WordPress install touches during a request goes through four steps: read from disk, tokenize (lex), parse into an abstract syntax tree, compile to opcodes, execute. A WordPress page load touches 100-200 PHP files. Without OPcache, these first four steps happen for every file on every request. With OPcache, the compiled bytecode is stored in shared memory after the first compilation. Subsequent requests skip steps one through four and execute the already-compiled bytecode directly from RAM.

The practical impact on a WordPress site with 20 active plugins: PHP compilation time per request drops from 40-120ms to near zero. This improvement compounds with page cache (which eliminates PHP entirely for cached pages) and object cache (which reduces database round trips). OPcache is not a substitute for page cache — it is a multiplier on every request that PHP does execute.

The Configuration Settings That Actually Matter

Key php.ini OPcache settings for WordPress production:
; Enable OPcache
opcache.enable=1

; Memory allocated for compiled code (in MB)
; 128-256 MB is appropriate for most WordPress installs with many plugins
opcache.memory_consumption=256

; Max number of files that can be cached
; Default 10000 is too low for complex setups; set to 20000+
opcache.max_accelerated_files=20000

; Check for file changes every N seconds (0 = never check, requires manual reset)
; Set to 0 on production, 1-2 on staging
opcache.validate_timestamps=0

; Reuse memory after cache fills up
opcache.revalidate_freq=0

; String interning memory (set to 16 MB)
opcache.interned_strings_buffer=16

; Recommended: enable for production
opcache.fast_shutdown=1

The most impactful single setting is opcache.validate_timestamps=0. When set to 1 (the default), PHP checks each cached file's timestamp on every request to see if the file changed. On a server with hundreds of PHP files, these stat() syscalls add up. Setting this to 0 tells OPcache to trust its compiled cache indefinitely. The tradeoff: you must manually reset OPcache after deploying code changes. For most WordPress sites that update via the admin dashboard, this reset happens automatically via the plugin API.

How to Verify OPcache Is Working

Check OPcache status via WP-CLI:
# Basic OPcache status
wp eval 'print_r(opcache_get_status(false));'

# Look for these key values in the output:
# opcache_enabled => 1 (it is on)
# memory_usage.used_memory => should be well below memory_consumption setting
# opcache_statistics.hits => increasing on repeated calls
# opcache_statistics.misses => should decrease as cache fills

# Alternative: use PHP's built-in function directly
wp eval 'var_dump(function_exists("opcache_get_status"));'
# Returns bool(true) if OPcache is available

After verifying OPcache is enabled and configured, one more thing to check: whether opcache.memory_consumption is set high enough that the cache is not full. A full OPcache evicts older entries and recompiles them, which creates random performance degradation that looks like an intermittent slow server. If memory_usage.used_memory is above 85% of memory_consumption, increase the allocation.

Cache Invalidation: The Hardest Part of Caching That Nobody Explains Properly

Phil Karlton famously said there are only two hard things in computer science: cache invalidation and naming things. He said this because cache invalidation has a fundamental tension: the longer you cache something, the faster your site is — and the longer visitors might see outdated content after you change it. Every caching decision is a choice about where on this spectrum to land.

Cache invalidation flow: WordPress publish event triggers selective URL purge on page cache, CDN cache, and object cache layers, then cache warming repopulates key pages

Stale Cache: When Fast Content is Wrong Content

Stale cache is a cached response that no longer reflects the current state of your content. You update a product price. The old price is cached. Visitors see the wrong price for the next 30 minutes until TTL expires. You fix a typo in a blog post. The corrected version does not appear until the cache clears. For a personal blog, stale cache for 30 minutes is acceptable. For a WooCommerce store running a flash sale, stale cache for 30 minutes can mean customers see the wrong price, add to cart, and reach checkout to find the price changed. That is a conversion killer.

The solution is not to turn off caching. It is to configure targeted invalidation: clear only the specific URLs affected by a content change, immediately after the change. This is what WP Rocket's cache purge-on-update does. This is what LiteSpeed Cache's event-triggered purge does. A "clear entire cache" approach on every post save is lazy invalidation — it solves the stale content problem by destroying your entire cache hit ratio after every small update.

The Correct Invalidation Strategy by Site Type

Blog / Content Site
On post publish/update: Purge the specific post URL, the homepage (if it shows recent posts), and any category pages that include this post.
On plugin update: Purge all cache.
On theme update: Purge all cache + clear browser cache via query string versioning on assets.
WooCommerce Store
On product update: Purge specific product URL, product category pages, shop page.
On price change: Immediate purge of affected product pages.
On stock change: Purge product page if "In Stock / Out of Stock" displayed in cached HTML.
Cart, checkout, my-account: Never cache — add to exclusion list permanently.
Membership / Subscription Site
Logged-in member pages: Never cache — bypass on wordpress_logged_in_* cookie.
Public marketing pages: Cache normally with standard TTL.
Mixed pages (public + member state): Cache the HTML shell; load personalized elements via AJAX after page load.

Cache Warming: Making Purge Painless

The problem with smart invalidation is the first request after purge. That request triggers full PHP and database execution on a cold cache, delivering your worst possible response time to the first visitor after each update. On a small site with 20 pages, this is barely noticeable. On a site with 5,000 pages where you clear the entire cache on a plugin update, you have 5,000 cold pages waiting to hammer your server simultaneously as the next morning's traffic arrives.

Cache warming solves this by crawling your pages programmatically after a purge, before real traffic arrives. WP Rocket's preloader crawls your sitemap immediately after any cache clear. FlyingPress warms on schedule and on-demand. LiteSpeed Cache can be configured to crawl after purge events. The correct setup: purge targeted URLs, immediately re-warm them by requesting them with a crawler, so the next real visitor always gets a cache HIT.

WooCommerce Dynamic Exclusions in Practice

The standard WooCommerce cache exclusion list that every major plugin pre-configures:

WooCommerce page exclusions for any cache plugin's exclusion list:
URLs to exclude from page cache:
/cart/
/checkout/
/my-account/
/order-received/
/add-to-cart/
/wc-api/
/?wc-ajax=*

Cookies that trigger cache bypass:
woocommerce_cart_hash
woocommerce_items_in_cart
woocommerce_recently_viewed
wp_woocommerce_session_*
wordpress_logged_in_*

Note: /shop/ and /product/* can be cached for logged-out visitors
without the above cookies. These are the high-traffic pages that
benefit most from page cache.

The decision on product pages requires nuance. Product pages can be cached for unauthenticated visitors without cart cookies. But if your product pages show dynamic elements like "X people viewing this," real-time stock counts, or personalized recommendations, those specific elements must be loaded via JavaScript after the cached page loads rather than embedded in the cached HTML.

Common Cache Problems and Exactly How to Fix Them

Every cache problem I have encountered in practice falls into one of two categories: cache is serving content it should not (wrong content to wrong user), or cache is not serving content it should (constant MISS, defeated cache). Both are diagnosable in under five minutes with the right approach. This section is the troubleshooting reference.

ProblemRoot CauseFix
Logged-in users see cached pagesPage cache not excluding authenticated sessionsEnsure cache plugin excludes wordpress_logged_in_* cookie from caching
Admin bar visible on cached pagePublic cached version captured admin bar in HTMLPurge cache after logging out; configure plugin to exclude admin bar markup
Stale CSS or JS after theme updateBrowser or CDN cached old asset versionsClear CDN cache; enable asset versioning (query string or filename hash)
WooCommerce cart shows wrong itemsCart page getting cached despite session contentAdd cart, checkout, my-account URLs to cache exclusion list in plugin settings
Checkout broken or empty after cacheCheckout page cached without session awarenessExclude checkout URL from page cache; verify woocommerce_cart_hash bypass rule
Plugin conflict causes 500 errorTwo cache plugins writing conflicting object-cache.php dropinDeactivate all cache plugins; reinstall one at a time; check wp-content/ for stale dropin files
Cache never hits (always MISS)Cache-Control: no-store set by origin or pluginCheck response headers with curl -sI; look for Set-Cookie on cacheable pages
Cache cleared on every page loadPlugin set to no-cache TTL, or another plugin purges on saveCheck cron jobs; review plugin settings; check for aggressive security plugins intercepting requests

Diagnosing Any Cache Problem With curl

Before touching any plugin settings, run this first. It tells you exactly what your server is returning and whether any cache layer is active.

Cache diagnostic workflow — run these in order:
# Step 1: Check cache status headers for any URL
curl -sI https://yourdomain.com/any-page/

# Key headers to look for:
# CF-Cache-Status: HIT / MISS / BYPASS / DYNAMIC (Cloudflare)
# X-Cache: HIT / MISS (Nginx, Varnish)
# X-LiteSpeed-Cache: hit / miss (LiteSpeed)
# X-WP-Rocket: served (WP Rocket)
# Age: [seconds] — how long this response has been in cache

# Step 2: Check what Cache-Control your server sends
curl -sI https://yourdomain.com/ | grep -i "cache-control\|x-cache\|cf-cache"

# Step 3: Check whether cookies are causing bypass
# Test the same URL with and without WooCommerce cookies
curl -sI https://yourdomain.com/shop/ \
  --cookie "woocommerce_cart_hash=ANYTHING" | grep -i cache

# If status changes to BYPASS with cookie present — WooCommerce bypass is working correctly
# If status is BYPASS without cookie — something else is causing bypass (check Cache-Control header)

# Step 4: Verify page source for cache markers (WP Rocket specific)
curl -s https://yourdomain.com/ | tail -20
# Look for 

Logged-In Users Seeing Cached Content

This is the most dangerous cache failure mode. A logged-in admin edits a draft, and a public visitor sees the draft because the cache did not differentiate between the authenticated request and the public one. This should not be possible with any properly configured cache plugin, but it happens when someone disables the "exclude logged-in users" setting or sets up a custom cache rule that overwrites the default bypass logic.

Fix: Verify your cache plugin's bypass settings include wordpress_logged_in_* in the cookie bypass list. Test by logging in, viewing a page, logging out in a different browser, and confirming the logged-out version looks correct. Never test cache behavior while logged in to WordPress admin — you are always getting the bypassed, uncached version.

The Admin Bar on Cached Pages Problem

This happens when a page is cached while an administrator was logged in and the admin bar was visible. The cached HTML includes the admin bar markup, and public visitors see the WordPress admin bar on the live site. This is a configuration error: the cache was not excluding logged-in users when the capture happened, or a plugin forced a cache rebuild while an admin was active.

Fix: Clear all page cache. Verify logged-in user exclusion is active. Rebuild cache by visiting pages while logged out. If using WP-CLI: wp cache flush followed by wp rocket clean (if using WP Rocket).

Plugin Conflicts: The Stale dropin Problem

When you switch from one cache plugin to another, the old plugin's object-cache.php and advanced-cache.php dropins in wp-content/ may remain. The new plugin tries to install its own dropins. Two plugins' dropins coexist. The result ranges from a site that silently serves incorrect cache to PHP fatal errors on every request.

Clean up stale cache dropins via WP-CLI:
# Check for dropin files
ls -la wp-content/advanced-cache.php
ls -la wp-content/object-cache.php

# If dropins exist from an old plugin you've deactivated:
# Delete them (after verifying your new plugin is active and will recreate them)
rm wp-content/advanced-cache.php
rm wp-content/object-cache.php

# Then let your active plugin recreate them:
# WP Rocket: deactivate and reactivate the plugin
# Redis Object Cache: wp redis disable && wp redis enable
# LiteSpeed Cache: go to LiteSpeed Cache > Manage > Enable Object Cache

Where to Go Next

Caching is the performance layer that sits in the middle of your entire infrastructure stack. To the left of caching is your hosting environment: the server quality, PHP-FPM pool size, MySQL buffer pool, and network latency that determines how fast your cache misses resolve. To the right is your asset delivery: whether images are optimized, whether JavaScript is deferred, whether the CDN is delivering static files from edge nodes close to your visitors.

The TTFB guide breaks down every millisecond of server response time and explains exactly which optimization fixes each phase. If your page cache is working correctly but TTFB is still above 300ms, the TTFB guide will identify which non-cache layer is the bottleneck. The CDN explainer covers how page cache and CDN cache interact, why Cloudflare APO caches WordPress pages at the edge rather than just assets, and when CDN hit ratios drop because of WooCommerce sessions. For the underlying server infrastructure that determines how fast cache misses resolve, the web hosting infrastructure guide explains CPU, RAM, disk I/O, and database configuration in detail. If you are running WooCommerce specifically, understanding database optimization alongside object cache is the next logical layer to address once caching is configured correctly.

WordPress Cache FAQ

Does WordPress cache automatically, or do I need a plugin?

WordPress has zero built-in page caching. Every page request triggers full PHP execution and database queries unless you add a caching layer. PHP OPcache (if your host has it enabled) caches compiled PHP bytecode automatically at the server level. But the page cache, object cache, and browser cache all require configuration. A plugin like WP Rocket, LiteSpeed Cache, or FlyingPress handles most of this with sensible defaults. On managed WordPress hosts (Kinsta, WP Engine, Cloudways, ScalaHosting), the host configures server-level page cache for you — but you still benefit from a plugin for browser cache headers and asset optimization.

What is the difference between page cache and object cache?

Page cache stores the complete rendered HTML output of a page as a static file. When the next visitor requests that page, the server returns the static file directly, skipping PHP execution and all database queries entirely. Object cache stores the results of individual expensive operations in memory: database query results, API responses, computed values, WordPress transients. Object cache still runs PHP and still builds the page, but saves time on the expensive operations within that process. Page cache is the bigger win for most sites. Object cache is critical for WooCommerce stores, membership sites, and any site where caching full pages is not possible because content changes per user.

Is LiteSpeed Cache free and does it work on all hosts?

LiteSpeed Cache is free, but it requires LiteSpeed web server or OpenLiteSpeed to use its most powerful features including page cache and object cache. On hosts running Apache or Nginx, LiteSpeed Cache still works for some features (browser cache headers, image optimization, minification) but the page cache engine is disabled. Hosts that run LiteSpeed include A2 Hosting, Hostinger (on some plans), and any cPanel host that has upgraded to LiteSpeed. If your host uses Apache or Nginx, WP Rocket or FlyingPress are the correct alternatives. Always confirm your server type before choosing a cache plugin.

How do I know if my WordPress cache is actually working?

Check the response headers for your site's pages. A properly cached page will have an X-Cache: HIT header (from Nginx or Varnish), a CF-Cache-Status: HIT header (from Cloudflare), or an X-LiteSpeed-Cache: hit header (from LiteSpeed). For WP Rocket, look for X-WP-Rocket in the response or check for a comment at the bottom of the page source like . The fastest diagnostic: run curl -sI https://yourdomain.com/any-page/ and look for cache status headers. If you see MISS on every request, caching is not working. If you see HIT on the second request, it is working correctly.

Should I use Redis or a file-based object cache for WordPress?

Redis is almost always faster for WordPress object cache than file-based alternatives. The reason is simple: RAM access is orders of magnitude faster than disk access. A Redis lookup takes microseconds. A file-based cache lookup requires disk I/O, even on NVMe. For most shared hosting plans where Redis is unavailable, file-based caching via W3 Total Cache's disk-enhanced mode is the practical alternative. But when your host supports Redis (ScalaHosting, Cloudways, WP Engine, Kinsta), enabling it is one of the highest-impact performance changes you can make for a WooCommerce store or any site with complex database queries.

How long should I set my cache TTL?

It depends on how often the content changes. For a static blog that publishes once a week, a 24-hour page cache TTL is reasonable. For a news site publishing hourly, a 30-minute TTL with auto-purge on publish is more appropriate. For WooCommerce product pages where prices or stock change frequently, a 10-30 minute TTL combined with auto-purge on product update is safer. For static assets like images and fonts, 1 year (31536000 seconds) is correct when using versioned filenames. The general principle: set TTL as long as you can tolerate stale content, then configure auto-purge to handle real content changes. A long TTL with smart purging is far better than a short TTL that keeps your cache permanently cold.

Why does my WooCommerce cart break when I enable page cache?

The cart breaks because a cache plugin is serving the same cached cart page to every visitor. Cart content is session-specific: it depends on what each visitor has added to their cart. When a cache plugin captures a page that includes the cart widget, it stores whatever was in the cart at capture time and serves it to everyone. The fix is excluding cart, checkout, and account pages from page cache. Every major cache plugin has a list of URLs and cookies that should bypass cache. For WooCommerce: exclude /cart/, /checkout/, /my-account/, and any URL where woocommerce_cart_hash or woocommerce_items_in_cart cookies are present. WP Rocket and LiteSpeed Cache handle WooCommerce exclusions automatically.

What is cache warming and when do I need it?

Cache warming is the process of pre-loading your cache by programmatically visiting pages immediately after a cache purge, so the first real visitor gets a cached response rather than triggering a cold backend generation. Without warming, after every deploy or cache clear, the first visitor to each page experiences full PHP execution time while the cache fills. For a site with a few dozen pages, this is acceptable. For a site with thousands of pages, a traffic spike during a cold cache can overwhelm the server. WP Rocket includes a cache preloader that crawls your sitemap automatically. FlyingPress has a preloader as well. LiteSpeed Cache can be configured to crawl on purge. On high-traffic sites, warming the cache immediately after a purge is not optional.

What does PHP OPcache do and should I verify it is enabled?

PHP OPcache stores compiled PHP bytecode in server memory. Normally, PHP reads a source file, parses it, compiles it to opcodes, and executes those opcodes on every request. OPcache skips the parse and compile steps after the first execution of each file. For a WordPress installation with 100+ plugin files loaded per request, this represents a significant CPU saving. To verify OPcache is enabled: create a file with and look for the opcache section. Or use WP-CLI: wp eval 'var_dump(opcache_get_status());'. Most quality hosts enable OPcache by default. If yours does not, it is worth requesting or switching hosts. OPcache is free, it ships with PHP, and not enabling it on a production WordPress server is leaving significant performance on the table.

Can I use multiple cache plugins at once?

You should not. Multiple cache plugins write conflicting WordPress dropin files to wp-content/, specifically object-cache.php and advanced-cache.php. When two plugins both try to install these files, the behavior is unpredictable: one plugin may overwrite the other's dropin, both may conflict, or you may get PHP fatal errors on every request. The most common conflict I have seen in practice: a site with WP Rocket active installs LiteSpeed Cache during a trial, and the two plugins fight over advanced-cache.php. The result is a broken cache configuration that looks functional until a traffic spike reveals it. Use one cache plugin. If switching, deactivate and delete the old plugin fully before activating the new one, and manually verify wp-content/ does not contain stale dropin files from the previous plugin.

Does caching help with TTFB?

Page cache dramatically reduces TTFB. Without page cache, TTFB includes PHP execution time (50ms to 1500ms depending on site complexity and server quality) plus database query time (10ms to 500ms). With page cache, TTFB drops to the time it takes the web server to read a static file from disk and return it, typically 10 to 50ms. Object cache alone reduces TTFB less dramatically because PHP still executes, but expensive queries are served from RAM instead of disk, cutting query time from hundreds of milliseconds to single-digit milliseconds. CDN cache (when combined with full-page caching like Cloudflare APO) reduces TTFB further by serving the cached HTML from an edge node close to the visitor. The full chain: page cache eliminates backend generation time, CDN cache eliminates geographic latency. Both together are the baseline for a fast WordPress site.

My host says they provide server-level caching. Do I still need a plugin?

It depends on what the host's caching actually does. Managed WordPress hosts (ScalaHosting with SShield, Cloudways with Breeze, WP Engine, Kinsta) typically handle page cache at the server level, and you do not need a separate page cache plugin. However, server-level hosting cache rarely handles asset minification, critical CSS generation, image lazy loading, or browser cache header optimization. A lightweight plugin that handles these tasks without installing its own page cache engine is a good complement to a host-managed cache. WP Rocket can detect an external page cache and disable its own page caching module while still handling everything else. Cloudways specifically recommends using Breeze (their plugin) alongside server-level Redis. Check your host's documentation before adding a cache plugin to avoid conflicts.