- There are four distinct caching layers — page cache, object cache, browser cache, and CDN cache — and you need all of them working together
- Installing a caching plugin only addresses one layer (page cache) and part of another (browser cache) — it’s not a complete caching solution
- Server-level page caching (Varnish, Nginx FastCGI) is 10–50x faster than plugin-based page caching because it bypasses PHP entirely
- Object caching with Redis can reduce database query time from 50ms per query to under 1ms — transformative for logged-in users and dynamic pages
- WooCommerce sites have unique caching challenges: cart, checkout, and account pages must bypass page cache while still benefiting from object cache and CDN
The Caching Misconception
Ask most WordPress site owners if they have caching set up and they’ll say “Yes, I installed WP Super Cache” or “I use WP Rocket.” They believe caching is a solved problem — a plugin they installed and forgot about.
It isn’t. What they’ve installed is one caching layer out of four. It’s like insulating your loft but leaving the windows open, the doors unglazed, and the walls bare. You’ve addressed one source of heat loss while ignoring three others.
Each caching layer solves a different performance problem. Each operates at a different level of the technology stack. And each has specific configuration requirements that most WordPress installations get wrong or ignore entirely.
Here’s how the full caching stack works — and why you need every layer.
Layer 1: Page Cache — Skip PHP and MySQL Entirely
What it does: Stores the fully-rendered HTML output of each page. When a visitor requests a page, the server returns the stored HTML directly instead of running PHP code, querying the database, and assembling the page from scratch.
Why it matters: A typical uncached WordPress page request involves: DNS lookup → TCP connection → PHP execution → 20–100 database queries → template assembly → HTML output. This takes 500ms–3 seconds depending on server specs and page complexity. With page caching, the entire PHP + database step is eliminated — the server simply reads a static HTML file from disk (or memory) and returns it. Response time drops to 20–50ms.

How it works — plugin-based: Plugins like WP Rocket, WP Super Cache, and W3 Total Cache generate static HTML files and store them in wp-content/cache/. When a request comes in, PHP checks if a cached file exists for that URL, and if it does, serves it instead of running WordPress. The problem: this still requires PHP to run (to check the cache), so you’re saving database time but not PHP boot time.
How it works — server-level: Varnish and Nginx FastCGI cache operate at the web server level, before PHP even starts. The web server itself checks its cache and returns the stored HTML. PHP never executes at all for cached pages. This is why server-level page caching is 10–50x faster than plugin-based caching — you’re eliminating the PHP overhead entirely, not just the database queries.
Implementation:
For plugin-based caching, install WP Rocket (paid) or WP Super Cache (free). Enable page caching, set cache lifespan to 10 hours for most sites, and configure cache preloading so pages are always warm.
For server-level caching, the setup depends on your hosting. Cloudways provides Varnish out of the box — enable it in the application settings. Kinsta uses Nginx FastCGI cache with their custom mu-plugin. If you manage your own server, Nginx FastCGI cache is the recommended approach — it requires adding fastcgi_cache directives to your Nginx configuration and a small WordPress plugin to handle cache purging when content updates.
Cache invalidation: The hardest part of page caching isn’t generating the cache — it’s knowing when to clear it. Content updates (new post, edited page, new comment) should purge the relevant cached pages. Menu changes and widget updates should purge all pages. WooCommerce stock changes should purge product pages. Most caching solutions handle this automatically, but verify by editing a page and confirming the change appears immediately.
Layer 2: Object Cache — Eliminate Repetitive Database Queries
What it does: Stores the results of database queries in memory (RAM). When WordPress needs data it has already fetched — user info, option values, post meta — it reads from memory instead of querying MySQL again.
Why it matters: A typical WordPress page load executes 30–100 database queries. Many of these are identical across page loads — the site title, active plugins, widget configurations, menu structures. Without object caching, WordPress queries the database for this information on every single page load, for every single visitor. With object caching, this data is fetched once, stored in memory, and reused until it changes.
The logged-in user problem: Page caching doesn’t help logged-in users. WooCommerce caching has unique challenges beyond what standard page caching can solve — see our dedicated WooCommerce speed optimisation guide for the full breakdown. Optimising — optimising TTFB for dynamic pages requires object caching. When you’re logged into WordPress — as an admin, editor, or WooCommerce customer — page cache is typically bypassed because the page content varies per user (admin bar, account info, cart contents). This means logged-in users get the full uncached experience: 30–100 database queries per page load. Object caching is the only caching layer that significantly improves performance for these users.
WordPress’s built-in object cache: WordPress has a built-in object cache API (wp_cache_set(), wp_cache_get()). By default, it uses PHP arrays — data is cached in memory during a single page request but lost when the request ends. This is called a “non-persistent” object cache. It eliminates duplicate queries within a single request but provides zero benefit across different requests.
Persistent object caching with Redis: Redis is an in-memory data store that persists data across requests. When you install a Redis object cache plugin (like Object Cache Pro or the free Redis Object Cache plugin), WordPress’s cache API stores data in Redis instead of PHP arrays. The data survives across page loads, so database queries executed during one visitor’s request are available to all subsequent visitors without re-querying MySQL.
Redis vs Memcached: Both are in-memory data stores suitable for WordPress object caching. Redis supports data structures (lists, sets, hashes), persistence to disk, and replication — making it more versatile and resilient. Memcached is simpler and slightly faster for basic key-value storage but lacks Redis’s features. For WordPress, Redis is the standard recommendation. Memcached is acceptable if Redis isn’t available on your hosting.
Impact: On a well-configured site with Redis object caching, database query time drops from 50–200ms to 2–10ms for cached queries. On WooCommerce sites with high traffic, this difference is transformative — it’s the difference between a site that slows under load and one that stays responsive regardless of traffic. Pair it with proper database optimisation for the best results.
Implementation: Most managed WordPress hosts offer Redis as an add-on. On Cloudways, enable Redis from the application settings, then install the Redis Object Cache plugin. On Kinsta, Redis is available on higher-tier plans. See our hosting comparison for which providers support each caching layer. On self-managed servers, install Redis via your package manager, configure the connection in wp-config.php, and install a persistent object cache drop-in.
Layer 3: Browser Cache — Make Return Visits Instant
What it does: Tells the visitor’s browser to store static files (CSS, JavaScript, images, fonts) locally. On subsequent visits, the browser loads these files from its local cache instead of downloading them again from your server.
Why it matters: A first-time visitor to your site downloads everything — HTML, CSS, JavaScript, images, fonts. This might total 1–3MB. Without browser caching, a return visitor downloads all of that again. With proper browser caching, the return visit only downloads the HTML document (typically 20–50KB), because everything else is already stored locally. Return page loads drop from seconds to milliseconds.
How it works: Browser caching is controlled by HTTP headers sent with each response. The two main headers are:
Cache-Control: max-age=31536000, immutable — tells the browser to cache this file for one year and never revalidate. Used for versioned static assets (files with hash in their filename, like style.a1b2c3.css).
Cache-Control: no-cache — tells the browser to always revalidate with the server before using the cached version. Used for HTML documents, where content changes frequently.
Common misconfiguration: Setting short cache times (1 hour, 1 day) for static assets like CSS and images. If your CSS file is style.css?ver=1.0, set the cache time to one year. When you update the CSS, change the version query string to ?ver=1.1 — this forces browsers to download the new version while keeping the old version cached until it expires naturally.
Implementation: On most hosting, add cache headers via .htaccess (Apache) or the Nginx configuration. Caching plugins like WP Rocket configure browser caching automatically. Cloudflare also handles this at the CDN level. The key settings: HTML = no-cache, CSS/JS = 1 year (with version strings), images = 1 year, fonts = 1 year.
Layer 4: CDN Cache — Deliver Content From the Nearest Server
What it does: Copies your site’s static assets (and optionally full pages) to servers distributed across the globe. When a visitor from Tokyo requests your London-hosted site, they receive files from a Tokyo-based CDN server instead of making a round trip to London.

Why it matters: The speed of light is a hard limit. A request from Sydney to a London server takes a minimum of 130ms for the round trip, just for the data to travel the physical distance. With a CDN, that same request hits a Sydney-based server with a round trip of 5–10ms. Multiply this by the 20–50 requests a typical page makes, and CDN reduces total latency by 1–3 seconds for geographically distant visitors.
Static CDN: The basic CDN setup caches static files — CSS, JavaScript, images, fonts — at edge servers worldwide. Your HTML still comes from your origin server. This is how most CDN setups work with WordPress and it’s a significant improvement for global visitors.
Full-page CDN (Edge Caching): Advanced CDN configurations cache the entire HTML page at edge servers. Cloudflare APO (Automatic Platform Optimisation) does this for WordPress. Instead of every request going to your origin server, the CDN serves the complete cached page from its nearest edge server. Your origin server only handles cache misses and logged-in users. This is the fastest possible configuration — TTFB drops to 20–50ms worldwide.
Implementation: Cloudflare’s free tier provides a global CDN with automatic static file caching. Point your domain’s DNS to Cloudflare, and static assets are cached automatically. For full-page edge caching, enable Cloudflare APO ($5/month for WordPress) or configure page rules to cache HTML with appropriate bypass rules for logged-in users, cart pages, and checkout.
BunnyCDN is an excellent alternative — faster than Cloudflare in many regions, simpler pricing, and easy WordPress integration via the BunnyCDN plugin. For WooCommerce sites, BunnyCDN’s lack of complexity can be an advantage because there are fewer cache bypass rules to configure.
How All Four Layers Work Together
Understanding each layer in isolation is straightforward. Understanding how they interact is where most configurations go wrong.
Here’s the request flow for a properly cached WordPress site:
Visitor requests a page → CDN edge server checks its cache. If the full page is cached at the edge (Layer 4), it’s returned in 20–50ms. Done. The origin server never sees the request.
CDN cache miss → Request reaches origin server → Server-level page cache (Varnish/Nginx) checks its cache. If the page is cached (Layer 1), the stored HTML is returned in 10–30ms. PHP never executes. The CDN stores this response for next time.
Server page cache miss → PHP starts → WordPress boots → Object cache (Redis) provides stored data. WordPress assembles the page, but most database queries are served from Redis (Layer 2) instead of MySQL. Page generation takes 100–300ms instead of 500–3000ms. The result is stored in the page cache and CDN for next time.
Visitor’s browser receives the response → Browser stores static assets locally (Layer 3). Next time this visitor returns, CSS/JS/images load instantly from local cache. Only the HTML document is re-requested.
When all four layers are working correctly, the typical experience is: first visit from a region = 200–400ms (CDN cache miss, page cache hit). Subsequent visits from same region = 20–50ms (CDN cache hit). Return visits from same browser = near-instant (browser cache for assets, CDN cache for HTML).
Common Misconfigurations That Break Caching
Caching logged-in pages: Page cache must be bypassed for logged-in users. If your caching setup serves cached pages to logged-in admins, they won’t see the WordPress admin bar, won’t see draft previews, and WooCommerce customers will see other people’s cart contents. Every caching solution needs a cookie-based bypass rule for logged-in users.
Caching WooCommerce cart and checkout: These pages contain user-specific data (cart contents, shipping calculations, payment forms). They must never be page-cached. Most caching plugins handle this automatically for WooCommerce, but verify by adding items to your cart, checking out, and confirming the pages show your actual cart contents. CDN edge caching is particularly tricky here — ensure your CDN bypasses cache for WooCommerce session cookies.
Cache-Control header conflicts: When multiple systems set cache headers (WordPress, your caching plugin, Nginx, Cloudflare), the resulting headers can conflict. Use curl -I your-url.com to inspect the actual headers received by browsers. Look for duplicate or contradictory Cache-Control directives.
Stale cache after updates: You edit a page, save it, visit the site — and see the old version. This means your cache purging isn’t working. Check that your caching plugin purges on content save, that Varnish/Nginx receives purge requests, and that your CDN is configured for automatic purging (Cloudflare APO does this; manual CDN setups may not).
Query string caching: Some CDNs don’t cache URLs with query strings (?utm_source=google). This means every marketing campaign link bypasses your CDN cache. Configure your CDN to ignore marketing query parameters (utm_*, fbclid, gclid) when determining cache keys.
Which Hosting Providers Support Which Caching
WooCommerce Caching Challenges
WooCommerce makes caching significantly more complex because large portions of the site contain user-specific, dynamic content.
Pages that must bypass page cache: Cart, checkout, my account, and any page with a [woocommerce_cart] or [woocommerce_checkout] shortcode. These pages display user-specific data that cannot be cached.
Cart fragments: WooCommerce uses AJAX (specifically, the wc-ajax=get_refreshed_fragments endpoint) to update the mini-cart widget on every page load. This AJAX request hits the server on every page view, even for pages that are otherwise fully cached. It’s one of the most common causes of high server load on WooCommerce sites. Solutions: disable cart fragments on pages without a mini-cart, or load them on interaction (hover/click on cart icon) instead of on page load.
Object caching for WooCommerce: Redis object caching is even more impactful on WooCommerce than on standard WordPress sites. WooCommerce executes significantly more database queries per page — product data, pricing, stock levels, tax calculations, shipping zones. Redis caching can reduce WooCommerce page generation time by 60–80%.
CDN for WooCommerce: Use a static CDN for product images, CSS, and JS. Full-page CDN caching is possible for product listing and product detail pages but requires careful configuration to bypass cache when WooCommerce session cookies are present. Get this wrong and customers see other people’s carts — a serious privacy and trust issue.
Frequently Asked Questions
Do I need a caching plugin if my host has server-level caching?
Not for page caching — server-level caching (Varnish, Nginx FastCGI) is faster than plugin-based page caching. However, caching plugins often provide additional features like browser cache headers, CSS/JS minification, critical CSS generation, and lazy loading. If your host handles page caching, you can use a lighter plugin (or no plugin) for the remaining optimisations.
How do I know if my caching is actually working?
Check HTTP response headers using curl -I your-site.com. Look for: X-Cache: HIT (Varnish), X-FastCGI-Cache: HIT (Nginx), cf-cache-status: HIT (Cloudflare), and Cache-Control headers with appropriate max-age values. If you see “MISS” consistently, your cache isn’t working or isn’t being warmed properly. Your TTFB should be under 100ms for cached pages.
Will caching help with my Core Web Vitals scores?
Page caching and CDN caching directly improve Time to First Byte (TTFB), which is a foundational metric that affects both Largest Contentful Paint (LCP) and First Contentful Paint (FCP). Browser caching improves performance for return visitors. Object caching helps with uncacheable pages. However, caching won’t fix render-blocking CSS/JS, unoptimised images, or layout shift — those require separate optimisations.
Is Redis worth the extra cost?
For any site with more than a few hundred daily visitors, yes. Redis object caching typically costs $0–10/month extra on managed hosting. The performance improvement — especially for logged-in users, admin dashboard speed, and WooCommerce sites — is substantial. For high-traffic sites, it’s the difference between needing a $50/month server and needing a $200/month server.
Can too much caching cause problems?
Yes. Over-aggressive caching can serve stale content after updates, show cached pages to logged-in users (privacy issue), cache WooCommerce cart pages (showing other people’s carts), or prevent A/B testing tools from working. The key is configuring proper cache bypass rules for dynamic and user-specific content, and ensuring cache invalidation fires reliably when content changes.
Caching configured wrong — or not at all?
VeloPress configures all four caching layers for every client
Server-level page cache, Redis object cache, optimised browser cache headers, and CDN with edge caching. Properly configured, properly tested, properly maintained.