WooCommerce plus a CDN should be a simple win on paper: static assets served from a nearby edge, HTML pages cached aggressively, customers across the world seeing the same fast load times regardless of where the origin server lives. In practice, configuring a CDN for WooCommerce without breaking the cart, the checkout, the account pages, or the live stock display is harder than the plugin docs suggest. The core issue is that parts of a WooCommerce site are dynamic per user, parts are static, and a CDN that cannot tell them apart will either serve stale pages to real users or refuse to cache anything useful at all.
I have configured CDNs on probably forty WooCommerce stores over the years, and the patterns of what goes wrong are remarkably consistent. The first time I set up Cloudflare on a client store back in 2019, I caused a 24-hour incident where customers were seeing each other’s cart contents because the cache was indexing on URL alone and ignoring the WooCommerce session cookie. That kind of mistake teaches you to be careful in a way that no documentation does. This guide is the configuration that actually works, the version I now use as a baseline on every new client store, and it assumes Cloudflare as the CDN because it is the most common choice for WooCommerce in 2026. The principles apply to any full-page-capable CDN including BunnyCDN, KeyCDN with their edge cache feature, or Fastly.
What to cache versus what not to cache, in one table
The cache strategy for a WooCommerce site needs to be explicit about which content is cacheable and which is not. Get this table right and most of the rest of the configuration follows.
| Content | Cache? | Notes |
|---|---|---|
| Static assets (CSS, JS, images, fonts) | Yes, long TTL of 30 days or more | Standard, the easy win, never controversial |
| Product pages | Yes, moderate TTL of 4 to 8 hours | Invalidate on product save and stock change |
| Product archive and category pages | Yes, 1 to 4 hours | Invalidate on any product save in the category |
| Homepage | Yes, 15 to 60 minutes | Dynamic banners may need an edge-side include |
| Blog and content pages | Yes, 24 hours | Standard, low risk of staleness issues |
| Cart page | No | Per-user content, must never be cached |
| Checkout | No | Per-user, must never be cached, ever |
| My Account | No | Per-user, must never be cached |
| WP Admin | No | Obviously not, do not even consider it |
REST API /wp-json/wc/* | No | API responses are per-user or live stock |
wp-login.php | No | Login is not cacheable, full stop |
The CDN has to enforce all of this through a combination of bypass rules and cookie-based cache skipping. The shorthand for the policy is “cache almost everything except these specific paths and these specific cookie states,” and the rest of this post walks through how to encode that policy in Cloudflare specifically.
Cloudflare base configuration that actually works
Step 1: enable full caching via Cloudflare’s Cache Rules. This requires Cloudflare Pro or higher, or you can fake it with Cache Everything via Page Rules on the Free tier with some limitations.
Create a cache rule matching all GET requests to the site, set “Cache Eligibility” to “Eligible for cache,” and set the Edge TTL to 2 hours. This is the base layer: everything is cacheable unless a bypass rule kicks in to skip it. Without this base rule, none of the bypasses below have anything to bypass against.
Step 2: bypass caching for WooCommerce-sensitive paths. Create a bypass rule with the URI Path matching any of the following dynamic paths:
Set the action to Bypass cache. These URLs are never cached at the edge under any circumstances, regardless of the cookie state or the user’s logged-in status. This is the safety floor that prevents the worst-case incidents.
Step 3: bypass cache for logged-in users entirely. Create another rule that triggers if the request contains any of the WordPress login cookies or the WooCommerce session cookies, like wordpress_logged_in_*, wp_woocommerce_session_*, woocommerce_items_in_cart, or woocommerce_cart_hash. Bypass cache entirely when any of these cookies are present. This ensures that a logged-in customer or a site admin never sees a cached anonymous version of a page that would expose other users’ state to them.
Cloudflare’s syntax for the cookie check looks like this:
Step 4: long TTL for static assets. For paths matching /wp-content/uploads/*, /wp-content/plugins/*, and /wp-content/themes/* with file extensions in the set (css|js|png|jpg|jpeg|gif|webp|svg|woff|woff2|ttf|eot|ico), set the edge TTL to 30 days and the browser TTL to 7 days.
WordPress automatically appends version query strings (?ver=) to assets when they change, so long TTL is safe in practice. A theme or plugin update generates a new URL, the old cached entry becomes irrelevant immediately, and the new URL gets cached fresh from origin on the first request.
Purging the cache on product and stock changes
A product that just sold out should not show “In Stock” for another two hours because of edge cache. The CDN has to be purged on specific WooCommerce events, and “the customer will refresh” is not a defence I have ever found acceptable. The purge needs to be automatic and immediate.
Install a plugin that hooks WooCommerce events to the Cloudflare API. Super Page Cache for Cloudflare and W3 Total Cache both do this well, and either is fine for most setups. Or, if you prefer to keep your dependencies small and roll your own, the pattern looks like this:
The cloudflare_purge_urls function calls the Cloudflare API. Use a Zone ID and an API Token with the Cache Purge permission, and store both in environment variables rather than hardcoding them in your custom plugin. The pattern fits naturally with the broader caching architecture I covered in my WooCommerce caching strategies guide, which covers how the edge cache layer fits with object caching and page caching at the WordPress layer.
The dynamic content in cached pages problem
Even on a cacheable product page, some of the content is dynamic per user: a mini-cart icon with the current item count, a “Welcome, Jane” greeting in the header, a recently-viewed products widget, a personalized recommendation block. If these elements render server-side into the cached HTML, the cache either becomes per-user (defeating the purpose entirely) or shows stale data to real users.
Three patterns to solve this, in order of how often I use them:
Pattern A: AJAX replacement. Render a placeholder in the cached HTML, something like “cart count: …”, and then on page load fire an AJAX request to a non-cached endpoint that returns the user-specific value. JavaScript replaces the placeholder with the real number once the response lands. This is by far the most common pattern, and WooCommerce’s built-in wc-ajax=get_refreshed_fragments is exactly this approach for the mini-cart specifically. I use this on every WooCommerce site I configure.
Pattern B: Edge Side Includes. Cloudflare Workers and Fastly both support ESI, where the cached HTML contains <esi:include> tags that the edge resolves from a separate uncached endpoint at request time. This is more complex to configure than the AJAX pattern, and I would only reach for it on large-scale deployments where the page-level cache hit rate is more important than the slightly-higher complexity cost.
Pattern C: JavaScript local state. Store the user’s cart in localStorage on every update and render cart counts client-side from localStorage rather than from a server roundtrip. This is fast in the happy case but brittle in the long run, because localStorage can diverge from server-side truth if the user clears their browser data or uses another device. I avoid this pattern on serious commerce sites and only use it on the simplest possible scenarios.
For most WooCommerce sites, Pattern A is the right choice and the rest of the patterns are corner cases. It is what WooCommerce itself uses out of the box for the mini-cart, and you should not fight that decision unless you have a specific reason to.
Image optimization at the edge
Cloudflare Polish (Pro tier and above) and Mirage (Pro tier and above) auto-optimize images at the edge: conversion to WebP or AVIF formats, intelligent compression based on the requesting device, and responsive sizing for different viewports. The cheaper alternative is to use ShortPixel or EWWW Image Optimizer to convert images at the WordPress layer once, and then serve them via Cloudflare with a long TTL. Both approaches work and both improve Largest Contentful Paint on product pages substantially.
This is usually the biggest Core Web Vitals win on a typical WooCommerce store, because raw image weight is the most common bottleneck I see during performance audits. If you are about to start tuning a slow store, image optimization is the first thing I would look at, and the rest of the database tuning patterns I covered in my WooCommerce database optimization guide only matter once the asset weight is under control.
Global origin and cache hit rate notes
If your origin server is in one geographic region (say, US East) and most of your customers are in Europe or Asia, the cache hit rate matters more than the origin distance. A well-cached page served from an edge near the customer is fast regardless of where the origin lives. A cache miss, on the other hand, means a full round trip to origin, which is where the regional latency penalty actually hits. Your job as the operator is to maximize cache hit rate so that the round trip happens as rarely as possible.
To improve cache hit rate in practice:
- Longer TTLs wherever it is safe to set them, balanced against the freshness requirements of the specific content type
- Warm the cache after every deploy by curling every important URL to populate the edge before real traffic arrives
- Monitor Cloudflare analytics for the cache hit ratio and target 85 percent or higher on product pages, which is achievable on most stores
Cloudflare Argo Smart Routing, which is a paid addon at around $5 per month for small sites, improves the origin-to-edge latency on cache misses meaningfully. Worth the spend on small sites and definitely worth the spend on larger ones.
Common mistakes that I keep seeing
- Forgetting the WooCommerce session cookie in bypass rules. Result: customers with items in cart see empty-cart cached pages intermittently, lose trust, and bounce.
- Caching the REST API. WooCommerce uses
/wp-json/wc/store/cartfor live cart state, and caching that endpoint completely breaks the Store API and any headless or block-based checkout that depends on it. - TTLs set too long without a purge-on-stock hook. Customers see in-stock items that have actually sold out, complete the checkout flow, and then hit an “out of stock” error at the payment step. This is bad UX and an even worse trust signal for the brand.
- Not testing the logged-in versus logged-out experience separately. The site owner is always logged in while testing, the cache rules fire bypass for them, so they never actually see the cached anonymous experience that real customers are getting.
- Cloudflare Flexible SSL on a WooCommerce site. Always use Full Strict SSL on commerce sites. Flexible SSL creates redirect loops on the checkout flow under specific configurations, and the failure mode is intermittent and hard to debug.
A minimal Cloudflare page-rule set for a small store
If you are still on Cloudflare Free and cannot create unlimited cache rules yet, here is the absolute minimum page-rule set that keeps the dynamic flows safe:
- Page rule matching
example.com/cart/*, set Cache Level to Bypass - Page rule matching
example.com/checkout/*, set Cache Level to Bypass - Page rule matching
example.com/my-account/*, set Cache Level to Bypass - Page rule matching
example.com/wp-admin/*, set Cache Level to Bypass and Security Level to High
Then use a plugin like Super Page Cache for Cloudflare to handle the cookie-based bypass and the cache-everything logic via its plugin integration with the Cloudflare API. Upgrading to Cloudflare Pro for the full Cache Rules feature set is the cleaner long-term choice once your traffic justifies the spend, and the spend is trivial compared to the performance gains.
Pre-launch readiness for high-traffic events
If you are configuring a CDN ahead of a known traffic spike, like a Black Friday sale or a product launch that you know is going to hit social media, the CDN configuration alone is not enough. You also need to think about origin capacity, queue handling, and the rest of the high-traffic playbook I covered in my WooCommerce high-traffic sales optimization guide. The CDN reduces origin load, but it does not eliminate it, and a busy checkout flow still hits origin for every order.
The payoff for the configuration work
A properly configured CDN on a WooCommerce store delivers, in my experience: a 200 to 400 millisecond improvement in Time to First Byte on cached pages, a 40 to 60 percent reduction in origin server load, consistent global performance regardless of where customers are coming from, and built-in DDoS protection as a free side benefit. The cost is a few hours of careful configuration up front and a monthly Cloudflare bill that starts at zero on the Free plan and scales reasonably as the site grows.
The configuration is one-time work. The performance improvement compounds every single day the site is live, and the customer experience improvement is something you can feel in the conversion numbers within a couple of weeks of going live with the new setup. There are very few things in WooCommerce performance work where the ROI math is this clear, so do the work, do it carefully, and get it right the first time.

