xSpeed Cache is officially live! Get 50% OFF during Launch Week — Lifetime starts at just $79Lifetime from $79 — 50% OFF, Launch Week! See Plans →

See Plans →
Features
xSpeed Hub Pricing Docs Blog Scan
Appearance
Get Plugin
All articles
WooCommerceCachingCheckoutTutorialWordPressplatform:woocommercefix:cache-exclusions

WooCommerce Cache Exclusions Decide What Gets Stored, Not What Gets Served

xSpeed Cache Team Updated Sep 23, 2026 10 min read
Cover reading "Cache exclusions decide what gets stored, not what gets served", with the stat one of three serving layers reads your URL exclusion list

Updated September 2026

Our page cache ships seventeen default excluded URL patterns and twenty default excluded cookies, read from CacheModule.php on 23 September 2026, and it keeps a stored page for 168 hours unless you change that. Those two numbers decide what a mistake costs. A cart page written to disk before you set your exclusions is served to strangers for a week.

The order people expect is: turn caching on, load the shop, see what breaks, add exclusions. The order that works is the reverse, and the reason is mechanical rather than cautious. An exclusion rule governs what gets stored, and almost nothing in the serving path re-reads it. This guide sets a WooCommerce store up in the order that cannot leak a basket, with real menu paths and shipped defaults. Every step works on the free tier.

Quick Summary: What to Do Before the Master Switch

If you want…Do thisWhy
A store that never leaks a cartSet exclusions, then enable cachingStored pages are never re-checked against later rules
Custom or renamed checkout pages coveredAdd their paths to the URL exclusion listWooCommerce’s flag reads three page IDs, not URLs
The header cart total to stay correctKeep the cart cookie in the cookie listCookie rules reach the web server; URL rules do not
To undo a bad cachePurge, fix the rule, purge againEditing a rule leaves pages already on disk in place
Geolocated prices and caching togetherUse Geolocate (with page caching support)Plain geolocation varies on what the key cannot see

Three Layers Answer a Cart Request, and They Know Different Things

A file-based page cache is not one decision. It is three, taken at different distances from WordPress, and each layer knows less than the one behind it. This shape is common to every page cache that writes HTML to disk, and it is why exclusion rules behave as they do.

Three serving layers stacked from web server to PHP, each labelled with the rules it can read

LayerWhen it answersWhat it can readWhat it cannot read
Web serverBefore PHP startsOne bypass cookie name, a hardcoded cookie floor, the user-agent listYour URL exclusions
Drop-inBefore WordPress loadsA baked cookie pattern, a baked user-agent pattern, the lifetimeYour URL exclusions
WordPressThe full requestEverything, including DONOTCACHEPAGE and the URL list

Our advanced-cache.php carries exactly five baked-in values: a cookie pattern, a user-agent pattern, the default lifetime, the edge headers and the hit log path, verified from source on 23 September 2026. No URL list is among them, because there was never meant to be one. A URL exclusion stops the page being written, and if the write never happens no serving layer needs the rule.

That design has one consequence worth planning around. Add /checkout-2 to your exclusions today and every copy stored yesterday stays on disk, served by a layer that has no idea the rule exists, until it expires or you purge.

Cookies work the other way. When PHP decides a visitor must not be served from cache it sets a wordpress_no_cache cookie, so the web server can enforce the whole rule list by testing one name. Our source states the limit plainly: that only covers visitors PHP has seen at least once.

What WooCommerce Already Does, and Where It Stops

WooCommerce defends itself. WC_Cache_Helper::set_nocache_constants() defines DONOTCACHEPAGE, DONOTCACHEOBJECT and DONOTCACHEDB, and prevent_caching() merges no-store headers onto the cart, checkout and account pages, per the class source fetched on 23 September 2026. It reads those three pages from the woocommerce_cart_page_id, woocommerce_checkout_page_id and woocommerce_myaccount_page_id options.

Our comparison of twelve caching plugins and what each does about a cart covers which honour that flag. Two gaps survive whichever you pick:

  • A second checkout, a one-page checkout plugin or a custom thank-you route is not one of the three pages, and gets nothing.
  • The flag fires during a WordPress request. A page already on disk is served without one.

The geolocation setting that changes the answer

WooCommerce’s Default customer location control offers Geolocate and Geolocate (with page caching support), per class-wc-settings-general.php read the same day. Plain geolocation varies the page by visitor country while the cache stores one copy under one key, so the first shopper’s country is served to everyone.

The page-caching variant avoids that with a redirect. geolocation_ajax_redirect() issues a 307 to the same URL carrying a ?v= hash of the location, so each country lands on a distinct cache key, and update_geolocation_hash() stores a matching woocommerce_geo_hash cookie for an hour. If your store prices or ships by country, pick that mode before enabling caching.

The Default /cart Rule Matches More Than Carts

A bare pattern with no glob characters is a substring test against the path, so /cart matches /cart, /cart/items and anything else containing those five characters. That is deliberate: the source comment says the entry carries no trailing slash precisely so it catches both forms, which WooCommerce serves.

A substring match diagram showing which store paths the default slash-cart pattern catches

It also catches paths nobody meant to exclude:

PathMatched by /cartCached?
/cart/
/product-category/cartridges/
/blog/cartoons-we-love/
/product/ink-cartridge-set/
/shop/

An ink store loses its cartridge category to a rule written for baskets, and nothing reports an error. The fix is an anchored glob: /cart/* matches /cart/items and not /product-category/cartridges/. Adding any of *, ? or [ switches a pattern to anchored mode.

Inside a WordPress request the gates run in a fixed order, and that order explains a confusing symptom. Query-string rejection happens before URL exclusion, so /cart/?add-to-cart=12 is refused as a dynamic query rather than as an excluded URL, and your rule looks like it did nothing.

The gate order inside PHP, with the query-string check sitting ahead of the URL exclusion check

Set the Exclusions First

Open xSpeed Cache → Cache → Page Cache in your WordPress admin, or go straight to wp-admin/admin.php?page=xspeed#/cache/cache. Our page cache is free, built by WPDeveloper, our own company. Work top to bottom and leave the master switch until last.

  1. Read the shipped exclusions before adding any. Ours already carry /cart, /checkout, /my-account, /wc-api, /edd-api and /wp-login, so most stores add nothing here.
  2. Add every non-standard transactional path: a renamed checkout, a booking route, a custom account area. One pattern per line.
  3. Check the cookie list still keeps woocommerce_. That one prefix covers the cart, session and geolocation cookies.
  4. Set Cache Expiry deliberately. The default is 168 hours, and a shorter lifetime limits the blast radius of a rule you get wrong.
  5. Now turn the master switch on. That installs the drop-in and writes WP_CACHE into wp-config.php.
  6. Purge once, then shop your own store in a private window, with a real basket.

For a single page the free escape hatch is the editor sidebar: it writes an _xspeed_no_cache postmeta that beats every global rule, registered with the REST API so a script can set it too. The pattern-based rules and bypass engine matching by post type, category or author is Pro. A standard store does not need it, and the ordering rule applies whichever route you take.

Checking It Actually Worked

Start with the free xSpeed Scan. It needs no account, fetches your store from outside, and reports which cache answered and what the response headers said, which is the question this setup turns on.

If you would rather check by hand, request a page and read one header:

curl -s -o /dev/null -D - 'https://example.com/cart/' | grep -i x-xspeed-cache

BYPASS on the cart is what you want. HIT (nginx) or HIT (static) means the web server answered from disk without PHP, and on a cart page that is the leak. HIT (php) means the drop-in answered. Cache hits and misses lists every value.

One trap costs people an afternoon. curl -I sends a HEAD, and only GET requests are served from cache, so a HEAD reports BYPASS on every URL on the site. Send a real GET, as above. Our guide to confirming a cache is working covers the headers other plugins emit.

Then log out, add an item and load a cached page. If someone else’s basket appears, the diagnosis is in a logged-out visitor seeing someone else’s name.

Running This Across Stores You Did Not Build

Agency work inverts the problem. You inherit a store with caching on, an unknown rule list and pages of unknown age on disk. Purge first: that makes the rules you read the rules that apply.

wp xspeed purge clears every cache we own in one call, and wp xspeed cache inventory prints which pages are cached and how old, which finds a transactional page nobody excluded. xSpeed Hub runs both across a fleet. Our WooCommerce store notes list what differs from a content site; Free vs Pro shows where the free tier stops.

Where Stores Get the Order Wrong

  • Enabling the cache first and fixing it after. The pages stored in that window outlive the fix.
  • Editing a rule without purging. A rule change is never retroactive.
  • Testing while logged in. Logged-in visitors are never served cached pages, so the store looks right to you and broken to shoppers.
  • Leaving plain Geolocate on. It varies the page on something the cache key never carries.

Frequently Asked Questions

I added /checkout-2 to the exclusions and it is still served from cache. What went wrong?

The copies stored before the rule existed are still on disk, and the layer serving them never reads your URL list. Purge, load the page once, confirm BYPASS.

My printer-ink category stopped being cached and I never excluded it.

Check it against the default /cart pattern: /product-category/cartridges/ contains /cart, and a bare pattern is a substring test. Change it to /cart/*.

Do I still need exclusions if WooCommerce sets DONOTCACHEPAGE itself?

Yes. It fires during a WordPress request, and a page already on disk is served without one. It also covers only the three pages named in your settings.

My cart total in the header is wrong for logged-out shoppers.

That is the cookie rule, not the URL rule. Confirm woocommerce_ is still in the excluded cookie list, then check the web server layer, which only learns of visitors PHP has seen.

I purged, and the old page came back within a minute.

Something re-warmed it: a preloader, a crawler or your own visit rebuilds the page the moment it is purged. Fix the rule first, then purge.

Can I do this without xSpeed Cache?

Yes. Every page cache that writes HTML to disk has this three-layer shape, and WP Rocket, LiteSpeed Cache, WP Fastest Cache and WP Super Cache all ship WooCommerce exclusions in their free or bundled builds. Set them before enabling the cache, whichever you use.

Should I cache product pages?

Cache them. They are the pages a page cache helps most. Prices varying by customer group are the exception, and belong on the per-page override.

Does the block-based checkout change any of this?

The ordering rule does not change. The page IDs can, because a block checkout may sit on a different page from the shortcode one. Confirm which your store assigns.

Treat it as the last thing you touch. The shipped entries are prefixes of hash-suffixed real cookies, so tightening them serves shared pages to visitors who must never get one.

My staging site is fine and production leaks.

Compare the drop-in, not the settings screen. The cookie and user-agent rules compile into advanced-cache.php when caching is enabled and re-bake on each save, so a site whose switch was never re-flipped after a file restore holds an older pattern.

Conclusion: Order First, Speed Second

Your storeStart with
Standard WooCommerce pagesThe shipped defaults, unchanged
A renamed or second checkoutIts path, before the master switch
Prices by countryGeolocate (with page caching support)

What to do this week: purge the page cache, read the exclusion list you actually have, add any missing transactional path, switch a bare /cart to /cart/* if your catalogue has words containing it, then run the free scan.

Our page cache is free. Pro adds the pattern rules engine at $29 a year on the founding price, with a fourteen-day money-back guarantee and a lifetime option; our pricing page carries every tier. If checkout is slow rather than stale, optimising it is next.

Written by

xSpeed Cache Team

Try xSpeed Cache

Make your site load in milliseconds.

One switch. Zero bloat. Always free to start.