When a WooCommerce cart empties right after Add to cart, or checkout stops working after you turn on caching, one of two things is happening. Either a cache layer is serving a stored copy of a page that WooCommerce has to build fresh for each shopper, or a JavaScript optimization broke the scripts the checkout needs. Start by finding which layer served the stale copy, because the fix belongs in that layer and nowhere else.
A store usually has more than one cache layer: a plugin (WP Rocket, LiteSpeed Cache, WP Super Cache), sometimes a server cache, and sometimes Cloudflare in front of everything. Excluding the checkout in the plugin does nothing if Cloudflare is the one serving it.
Quick diagnosis
What you see | Most likely cause | Where to go |
|---|---|---|
The cart is empty right after Add to cart, or checkout says your cart is empty | The cart or checkout page was served from cache | Steps 1 to 4 |
A new visitor's mini cart already shows items, or the wrong count | A page was cached while a visitor had a cart, or the cart fragments request was cached | Mini cart shows the wrong items |
A customer sees another customer's name, address or cart | A personal page was cached and served to someone else | Purge every cache now, then Steps 1 to 4 |
Checkout spins, Place order does nothing, or payment fields never load | A JavaScript optimization (minify, combine, delay, Rocket Loader) | Checkout spins or Place order does nothing |
Password reset keeps sending you back to the login screen | The My account page is cached | Steps 2 and 3 |
Step 1: Find which layer served the page
Open the store in a private browser window, add a product to the cart, and go to the cart page. Open your browser's developer tools, switch to the Network tab, reload, and click the first request in the list (the page itself). Look at its response headers:
Layer | What to look at | What "served from cache" looks like |
|---|---|---|
Cloudflare | the |
|
LiteSpeed Cache on a LiteSpeed server | the |
|
WP Rocket | the last lines of the page source (View Source) | A comment that starts "This website is like a Rocket" and ends with |
Another plugin or your host's cache | headers or HTML comments that mention a cache | Check that tool's own documentation for its hit marker. |
While you are there, read the cache-control header. On the pages you assigned as Cart, Checkout and My account, WooCommerce replaces the headers with WordPress's no-cache set (opens in a new tab), so you should see no-cache, max-age=0 and private. If your cart or checkout shows a normal cacheable header instead, WooCommerce does not recognize that page. Go to Step 2 first.
You can run the same check from a terminal:
curl -sI https://example.com/checkout/ | grep -i -E "cf-cache-status|x-litespeed|cache-control"A terminal request has no cart cookie, so it shows what an empty-handed visitor gets. That is still enough to see whether the checkout page itself is cached.
Step 2: Make sure WooCommerce knows your cart and checkout pages
WooCommerce protects the cart, checkout and account pages by page ID, not by URL. Its cache helper checks whether the current page is the one assigned as Cart, Checkout or My account. Only on those pages does it send the no-cache headers and define the DONOTCACHEPAGE constant that tells caching plugins to skip the page. WP Rocket's automatic exclusion also uses the pages "as set in your WooCommerce settings" (opens in a new tab), and LiteSpeed warns that "misconfigured page associations in WooCommerce settings may cause pages to be classified incorrectly" (opens in a new tab).
This breaks when the checkout was rebuilt as a new page (a page builder template, a duplicated page, a funnel plugin's page) and the new page was never assigned. To WooCommerce and to your cache plugin, that page is ordinary content, so it gets cached like a blog post.
Go to WooCommerce > Settings > Advanced > Page setup and check that the Cart, Checkout and My account dropdowns point to the pages shoppers actually use. WooCommerce's documentation (opens in a new tab) covers each field. Save, purge every cache layer, and repeat the header check from Step 1.
Step 3: Exclude the pages and cookies in the layer that served the copy
WooCommerce's own caching guide lists three pages to keep out of any cache (opens in a new tab): Cart, My account and Checkout, because they "display information specific to the current customer and their cart." Several plugins do this for you:
- WP Rocket excludes the Cart, Account and Checkout pages and the WooCommerce REST API automatically. You won't see them in its settings.
- LiteSpeed Cache excludes My account, Checkout and Cart by default.
- WP Super Cache gets the same signal from WooCommerce and skips those pages by default.
- Anything else: add the three page URLs to the plugin's "never cache" list yourself.
If the pages were already excluded and the problem continues, the cart is probably leaking through cached other pages. A product page or the homepage was cached while a visitor had items in the cart. The fix is to stop serving cached pages to shoppers who have a cart, using WooCommerce's cookies:
Cookie | What WooCommerce uses it for |
|---|---|
| Tells WooCommerce when cart contents change |
| Tells WooCommerce when cart contents change |
| Links the visitor to their cart in the database, kept for 2 days |
| Powers the Recently Viewed Products widget |
| Remembers that the visitor dismissed the store notice |
In WP Rocket this is Advanced Rules > Never Cache Cookies. Add woocommerce_items_in_cart, and for the session cookie use the partial name wp_woocommerce_session_, as WP Rocket's guide (opens in a new tab) shows. Other plugins have an equivalent cookie exclusion field. Shoppers with a cart then get uncached pages, which costs some speed for them and keeps every other visitor on the cache.
Two more exclusions from the same WooCommerce guide:
- If your cache also does database caching, exclude
_wc_session_from it. - On Varnish, pass requests for
/cart,/checkout,/my-accountand any URL containing?add-to-cart=straight to WordPress. WooCommerce publishes the VCL for this.
Step 4: If Cloudflare says HIT on your cart or checkout
Cloudflare does not cache HTML by default (opens in a new tab). If cf-cache-status says HIT on a page, a rule told Cloudflare to cache HTML: a Cache Rule set to "Eligible for cache," or an older Page Rule with Cache Everything.
Look at the rule's Edge TTL setting. Both "Use cache-control header if present" options follow the origin's cache-control header, so WooCommerce's private, no-cache header keeps the checkout out. The problem is the third option, "Ignore cache-control header and use this TTL." Cloudflare says it will "completely ignore any cache-control header on the response." (opens in a new tab) With that option on, the checkout is cached no matter what WooCommerce sends.
The fix is a separate Cache Rule with Cache eligibility: Bypass cache. Cache Rules can match on cookies, and when two rules conflict, the last matching rule wins (opens in a new tab), so place the bypass rule below your cache-everything rule. An expression that covers the WooCommerce pages, the add-to-cart and AJAX requests, and shoppers who have a cart or are logged in:
(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.query contains "add-to-cart=")
or (http.request.uri.query contains "wc-ajax=")
or (http.cookie contains "woocommerce_items_in_cart")
or (http.cookie contains "wp_woocommerce_session_")
or (http.cookie contains "wordpress_logged_in_")Adjust the three paths if your store uses different slugs, then purge the Cloudflare cache and repeat Step 1. The cart and checkout should now show DYNAMIC. Cache Rules take precedence over Page Rules on the same path, so this rule also wins over an old Cache Everything page rule. Our Cloudflare setup guide shows where these settings live.
If the checkout shows a Cloudflare challenge or loops back to itself instead, that is a security setting, not the cache. See Cloudflare challenge loop on the WordPress login for how to find the rule responsible.
Mini cart shows the wrong items
WP Rocket documents two different causes (opens in a new tab) for a mini cart that shows items a new visitor never added, and View Source tells them apart:
- The wrong cart is already in the page source. A page was cached while a visitor had products in their cart, and the theme printed that cart into the HTML. Everyone who gets that cached page sees it. The cookie exclusion from Step 3 prevents it; WP Rocket also offers a helper plugin, "Prevent Cache Generation for Active Carts."
- The source is correct, but the mini cart is wrong after the page loads. The request that refreshes the mini cart (
?wc-ajax=get_refreshed_fragments) was cached. WP Rocket optimizes that request by default, only when the cart is empty. Its helper plugin "Deactivate WooCommerce Refresh Cart Fragments Cache" turns that off.
On LiteSpeed, the documentation recommends enabling ESI with WooCommerce (opens in a new tab), which serves the cart as a private block inside an otherwise cached page. ESI needs LiteSpeed Enterprise, LiteSpeed Web ADC or QUIC.cloud; OpenLiteSpeed does not support it. If your theme updates the mini cart without JavaScript, turn on Vary for Mini Cart in the plugin's WooCommerce tab. It keeps a separate cached copy of the mini cart for visitors whose cart isn't empty.
One more cause, if the mini cart stopped updating after a WooCommerce update rather than after a cache change. Since WooCommerce 7.8, the cart fragments script only loads where the Cart widget is rendered (opens in a new tab). A theme with its own mini cart that doesn't use the widget can stop updating. WooCommerce also notes that a mini cart on every page, combined with page caching, "will not update to show the active cart contents" on cached pages. Ask your theme's developer whether their mini cart loads the wc-cart-fragments script.
Checkout spins or Place order does nothing
If pages load but the checkout never finishes (a spinner that doesn't stop, a Place order button that does nothing, card fields that never appear), suspect JavaScript optimization before the page cache. Open the browser console on the checkout page and look for red errors.
- WP Rocket excludes every page it doesn't cache from all other optimizations too (opens in a new tab). If your checkout is recognized (Step 2), WP Rocket isn't touching its scripts, so check product pages, where Add to cart runs. WooCommerce's guide recommends avoiding JavaScript file minification (opens in a new tab) with WP Rocket.
- Any optimization plugin: turn off the JavaScript options one at a time (minify, combine, defer, delay until interaction), purge the cache after each, and retest Add to cart and checkout. The option that fixes it is the one to adjust or exclude the checkout scripts from.
- Cloudflare Rocket Loader changes how scripts load. Cloudflare's own advice is to disable Rocket Loader and retest (opens in a new tab) if you see JavaScript or jQuery issues.
Verify
- In a private window: add a product, and the cart shows it; the checkout loads with the correct totals.
- The response headers for the cart and checkout pages show no
HITfrom any layer, andcache-controlincludesprivateandno-cache. - In a second browser with an empty cart, the product pages and homepage don't show the first browser's cart.
- With your payment gateway in test mode, a test order goes through to the order-received page.
- The rest of the store still caches: in the empty-cart browser, product and shop pages show your cache's hit marker from Step 1 on a second load.
Rolling back
Everything in this guide is a setting: a page assignment, an exclusion, a Cloudflare rule, or an optimization toggle. None of it changes products, orders or customer data. If a change makes things worse, restore the value you noted before changing it, purge the caches, and retest. Deleting the Cloudflare bypass rule returns Cloudflare to its previous behavior.
Related fixes
- WordPress Speed Optimization: The Complete Guide: what each cache layer does, and why running two page caches causes problems.
- Cloudflare + WordPress: The Complete Setup Guide: where cache, rule and speed settings live in the Cloudflare dashboard.
- Why WordPress on aaPanel Has High TTFB (Even With LiteSpeed Cache): if the store is slow rather than wrong, and whether LiteSpeed Cache can cache pages on your server at all.
- Back Up WordPress Before Updating Plugins: a quick backup routine before you update the cache plugin or WooCommerce.
FAQ
Why does my WooCommerce cart empty right after I add a product?
The cart page you're seeing was built for a visitor with no cart and stored in a cache, so every shopper gets that empty copy. Check the cart page's response headers for a cache hit (Step 1). Make sure it's assigned as the Cart page in WooCommerce > Settings > Advanced, then exclude it in whichever layer served the cached copy.
Can I still cache the shop and product pages?
Yes. WooCommerce only asks you to keep Cart, Checkout and My account out of the cache. Shop and product pages can be cached, as long as anything personal on them (the mini cart, wishlists, recently viewed products) is loaded with JavaScript after the page arrives. WP Rocket notes that features written to print personal data in PHP instead are what show the wrong content on cached pages.
How do I clear the cache on a WooCommerce store?
Clear every layer, in order from the server outward: your cache plugin's purge option, then any server or host cache, then Cloudflare's Purge Everything if you use it. If you clear only the plugin while Cloudflare still holds a copy, the old page keeps being served and a correct fix looks like it didn't work.
A customer saw another customer's details. What should I do first?
Purge every cache layer immediately so the page stops being served, then work through Steps 1 to 4 to find which layer stored a personal page and why. The pages WooCommerce tells you to exclude are exactly the ones that show one customer's information, so a cached copy of any of them can reach the next visitor.
Every guide explains the symptom, likely causes, the safest order of checks and how to confirm the fix. How we check our guides.



