Cloudflare Cache Everything Rule Was Serving Cached WooCommerce Cart Pages
· 8 min read
A client messaged me on a Friday afternoon: "Customers are adding products to their cart but the cart page is empty. It's been happening since yesterday." The store was doing around 200 orders a day. Every lost cart was revenue walking out the door.
The site was a WooCommerce store on a VPS I manage, fronted by Cloudflare. I hadn't touched the Cloudflare config in months — the client had hired a "speed optimisation" freelancer the previous week to "make the site faster." That freelancer had added a Cache Everything page rule with no exclusions.
The symptom
Customers would add a product to their cart via the AJAX add-to-cart button. The mini-cart in the header updated correctly (it's loaded via a wc-ajax fragment request). But navigating to /cart/ showed an empty cart. Refreshing didn't help. Clearing cookies and starting over sometimes worked, sometimes didn't.
Checkout was similarly broken — the page would load but with no line items. Some customers reported seeing a previous order's data in the checkout form fields.
First checks
My instinct was a caching issue, but I needed to confirm which layer. This site had three potential caching layers:
- Redis object cache (server-side)
- nginx FastCGI cache (which I'd already configured with proper WooCommerce bypasses)
- Cloudflare (the CDN edge)
I started with the response headers:
curl -sI "https://store.example.com/cart/" \
-H "Accept: text/html" | grep -i "cf-cache-status\|x-fastcgi-cache\|age"
The response came back:
cf-cache-status: HIT
age: 847
x-fastcgi-cache: BYPASS
There it was. nginx was correctly bypassing (it had my WooCommerce cookie rules), but Cloudflare was caching the HTML of the cart page and serving it to everyone. The age: 847 meant this cached response was nearly 15 minutes old.
I ran the same check on checkout:
curl -sI "https://store.example.com/checkout/" \
-H "Accept: text/html" | grep -i "cf-cache-status"
cf-cache-status: HIT
Both /cart/ and /checkout/ were being served from Cloudflare's edge cache. Every customer was getting the same empty (or stale) HTML regardless of their session.
Why this happens
By default, Cloudflare only caches static assets — images, CSS, JavaScript, fonts. It does not cache HTML. This is safe for WooCommerce out of the box.
But the moment you add a Page Rule (or Cache Rule) with "Cache Everything," Cloudflare starts caching HTML responses too. If you don't explicitly exclude WooCommerce's dynamic pages, the CDN will serve a cached version of /cart/ to every visitor.
The first anonymous visitor to hit /cart/ after the cache expired would get a genuinely empty cart (no session cookie, no items). Cloudflare would cache that empty response. Every subsequent visitor — even those with items in their session — would receive that same cached empty cart HTML. The JavaScript mini-cart still worked because wc-ajax fragment requests weren't being cached (they used query strings), but the full page was broken.
Worse, if a logged-in customer happened to be the first to hit the page after expiry, their cart contents would be cached and served to the next visitor. That's a data leak.
Finding the offending rule
I logged into the client's Cloudflare dashboard and found the Page Rule:
URL: store.example.com/*
Cache Level: Cache Everything
Edge Cache TTL: 2 hours
No exclusions. No cookie bypass. Every single URL on the domain — including /cart/, /checkout/, /my-account/, and even /?wc-ajax=get_refreshed_fragments — was eligible for edge caching.
The fix
I deleted the blanket Cache Everything rule immediately. That stopped the bleeding within seconds (Cloudflare purged the cached entries as the rule was removed).
But the client did benefit from edge caching on product and category pages. The right approach isn't "no caching" — it's "cache what's safe, bypass what isn't."
Since Cloudflare deprecated Page Rules in January 2025, I rebuilt the configuration using Cache Rules.
Rule 1: Bypass cache for WooCommerce dynamic paths
Name: Bypass WooCommerce transactional pages
Expression:
(http.request.uri.path contains "/cart" or
http.request.uri.path contains "/checkout" or
http.request.uri.path contains "/my-account" or
http.request.uri.path contains "/wc-api/" or
http.request.uri.query contains "wc-ajax" or
http.request.uri.path contains "/wp-admin" or
http.request.uri.path contains "/wp-login")
Action: Bypass Cache
Rule 2: Bypass cache when WooCommerce cookies are present
Name: Bypass on WooCommerce session cookies
Expression:
(http.cookie contains "woocommerce_items_in_cart" or
http.cookie contains "woocommerce_cart_hash" or
http.cookie contains "wp_woocommerce_session_" or
http.cookie contains "wordpress_logged_in_")
Action: Bypass Cache
Rule 3: Cache product and category pages for anonymous visitors
Name: Cache eligible pages
Expression:
(http.request.uri.path contains "/product/" or
http.request.uri.path contains "/product-category/" or
http.request.uri.path eq "/shop/")
and not (http.cookie contains "woocommerce_items_in_cart")
and not (http.cookie contains "wordpress_logged_in_")
Action: Eligible for Cache
Edge TTL: 4 hours
Browser TTL: 10 minutes
Rule order matters in Cloudflare — bypass rules must come before cache-eligible rules. The bypass rules are evaluated first, so a logged-in customer with items in their cart will never be served a cached page, even if they're browsing /product-category/shoes/.
Verifying the fix
After deploying the rules, I verified with curl:
# Anonymous request to a product page — should be cached
curl -sI "https://store.example.com/product/example-shoe/" \
-H "Accept: text/html" | grep -i "cf-cache-status"
# Expected: cf-cache-status: HIT (after second request)
# Anonymous request to cart — should bypass
curl -sI "https://store.example.com/cart/" \
-H "Accept: text/html" | grep -i "cf-cache-status"
# Expected: cf-cache-status: DYNAMIC
# Request with WooCommerce cookie — should bypass even on product pages
curl -sI "https://store.example.com/product/example-shoe/" \
-H "Accept: text/html" \
-H "Cookie: woocommerce_items_in_cart=1" | grep -i "cf-cache-status"
# Expected: cf-cache-status: BYPASS
All three returned the expected values. Cart and checkout pages showed DYNAMIC (never eligible for cache). Product pages with a cart cookie showed BYPASS (eligible but skipped due to cookie rule).
What about Cloudflare APO?
If you're using Cloudflare APO (Automatic Platform Optimisation) with the WordPress plugin installed, it handles WooCommerce cookie bypass automatically. APO detects cookies like woocommerce_items_in_cart and wordpress_logged_in_ and serves from origin when they're present.
You can verify APO is working correctly by checking the cf-apo-via header:
curl -sI "https://store.example.com/" \
-H "Accept: text/html" | grep -i "cf-apo-via"
cf-apo-via: cache— served from edge (good for anonymous)cf-apo-via: origin,bypass— served from origin due to cookie (good for logged-in/cart)cf-apo-via: origin,no-cache— origin sentCache-Control: no-cache
If you use APO, do not layer a "Cache Everything" rule on top. APO already handles HTML caching with proper WooCommerce awareness. Adding cache rules on top overrides APO's cookie logic and reintroduces the exact problem I described above.
Prevention
I now include Cloudflare cache verification in my monthly maintenance checks for any site using Cloudflare. The check is a simple script:
#!/bin/bash
DOMAIN="store.example.com"
PAGES=("/cart/" "/checkout/" "/my-account/")
for page in "${PAGES[@]}"; do
status=$(curl -sI "https://${DOMAIN}${page}" \
-H "Accept: text/html" | grep -i "cf-cache-status" | awk '{print $2}')
if [ "$status" = "HIT" ]; then
echo "WARNING: ${page} returning cf-cache-status: HIT"
else
echo "OK: ${page} returning cf-cache-status: ${status}"
fi
done
If any transactional page returns HIT, something has gone wrong with the cache rules. I run this after any Cloudflare config change and as part of the monthly check.
I've also added a note to the client's maintenance documentation: "Do not modify Cloudflare cache settings without consulting me first." The $5/month APO subscription or a carefully scoped set of Cache Rules is worth the performance gain — but a blanket Cache Everything rule on a WooCommerce store is a guaranteed way to break checkout and leak customer data.
This is exactly the kind of invisible breakage that a maintenance plan catches before customers notice. If your store's cart or checkout is behaving oddly — especially after a "speed optimisation" — check those edge cache headers first.
