Speed & Caching

Cache Plugin Broke Your WooCommerce Checkout? What to Check First

Cart empty after Add to cart, wrong mini cart, or a checkout that spins? Find which cache layer served the page from its headers, then fix only that layer.

A browser developer tools Network panel open on a store checkout page, with the response headers of the page request visible and one cache header highlighted.
The response headers of the checkout request tell you which cache layer served it.

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 cf-cache-status header

HIT means Cloudflare served its stored copy (opens in a new tab). DYNAMIC means it did not try to cache the request; BYPASS means it tried and the origin said no.

LiteSpeed Cache on a LiteSpeed server

the x-litespeed-cache header

hit. A page LiteSpeed is correctly skipping shows x-litespeed-cache-control: no-cache (opens in a new tab).

WP Rocket

the last lines of the page source (View Source)

A comment that starts "This website is like a Rocket" and ends with Debug: cached@ and a timestamp, which is WP Rocket's caching footprint (opens in a new tab).

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:

bash
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

woocommerce_items_in_cart

Tells WooCommerce when cart contents change

woocommerce_cart_hash

Tells WooCommerce when cart contents change

wp_woocommerce_session_ (plus a unique suffix)

Links the visitor to their cart in the database, kept for 2 days

woocommerce_recently_viewed

Powers the Recently Viewed Products widget

store_notice[notice id]

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-account and 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:

Code
(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.

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 HIT from any layer, and cache-control includes private and no-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.

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.

Stefan Nistor

Founder, WP Fix Stack

Stefan runs WP Fix Stack. Every guide checks its commands, menu paths and limits against the official documentation of WordPress, PHP, the hosting panel, Cloudflare or the plugin involved, and links those sources so you can verify them. Fixes are ordered from the least risky step, each guide ends with a way to confirm the fix and roll it back, and guides are corrected, with a new Updated date, when the documentation or the software changes.

Every guide explains the symptom, likely causes, the safest order of checks and how to confirm the fix. How we check our guides.

On this page