Your homepage scores 95 in PageSpeed Insights. Your product pages load fine. Then a customer adds something to the cart and everything slows to a crawl.
This is the most common performance pattern in WooCommerce, and it is not a coincidence. The moment a session becomes a cart, WooCommerce stops serving cached pages and starts doing work on every request. Almost every slow-checkout complaint traces back to one of four causes.
Why cart and checkout behave differently from the rest of your site
A product page is the same for everyone, so it can be cached and served as a static file in milliseconds. A cart page is different for every visitor. WooCommerce sets a session cookie, and from that point on your caching plugin is instructed to bypass the cache entirely.
So cart and checkout run the full stack on every single request: PHP boots, WordPress loads, every active plugin initialises, database queries run. Whatever inefficiency your site has been hiding behind the cache is now happening in front of the customer, at the exact moment they are deciding whether to pay you.
That is the frame for everything below. Checkout performance is not a caching problem. It is a how much work does one uncached request cost problem.
Cause 1: Cart fragments
wc-cart-fragments.js is the script that keeps your mini-cart counter up to date without a page reload. It works by firing an AJAX request to admin-ajax.php.
Here is the part that surprises people: on many themes it fires on every page load, including the homepage, including blog posts, including pages with no cart element at all. And because it hits admin-ajax.php, the request is never cached.
The cost is real. A single cart-fragments call commonly adds several hundred milliseconds to Time to First Byte, and on shared hosting it can exceed a second. Multiply that by every page view on your site.
How to check: Open DevTools → Network, filter by admin-ajax, and load your homepage. If you see a request with get_refreshed_fragments in the payload, it is running site-wide.
What to do: Disable it where it serves no purpose — pages without a mini-cart — and keep it on cart, checkout and product pages. Some performance plugins offer this as a toggle. Doing it in code is more precise:
add_action( 'wp_enqueue_scripts', function () {
if ( is_woocommerce() || is_cart() || is_checkout() ) {
return;
}
wp_dequeue_script( 'wc-cart-fragments' );
}, 99 );
Test the mini-cart thoroughly afterwards. Some themes depend on this script for behaviour beyond the counter, and breaking the add-to-cart feedback loop costs more than the milliseconds you saved.
Cause 2: wp_options autoload bloat
This is the one nobody looks at, and it is frequently the largest single factor.
WordPress loads every option row marked autoload = yes on every request — one query, one array, before anything else happens. In a clean install that is a few hundred kilobytes. On a WooCommerce store that has been running for three years, it is routinely several megabytes.
Where it comes from: plugins that store transients without expiry, plugins you deleted years ago that never cleaned up, license keys, cached API responses, page-builder settings, abandoned-cart data. Every uninstalled plugin tends to leave rows behind, and nothing ever removes them.
The effect is a flat tax on every uncached request — which means every cart and checkout page view.
How to measure it:
SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS autoload_mb,
COUNT(*) AS rows_count
FROM wp_options WHERE autoload = 'yes';
SELECT option_name, ROUND(LENGTH(option_value)/1024, 1) AS kb
FROM wp_options WHERE autoload = 'yes'
ORDER BY LENGTH(option_value) DESC LIMIT 25;
What to do: Look at the top 25 and identify what you recognise. Rows prefixed with plugins you no longer run can go. Expired transients (_transient_%, _transient_timeout_%) can go. Large rows from active plugins should not simply be deleted — set autoload to no where the data is not needed on every request, or fix the setting in the plugin itself.
Back up the database first. wp_options is where WordPress keeps everything that matters, and there is no undo.
Cause 3: Payment gateway requests during page load
Some payment plugins make outbound HTTP calls while your checkout page is rendering — fetching available payment methods, validating credentials, initialising a session token, checking currency support.
The consequence: your checkout is now only as fast as the slowest third-party API in the chain. If Stripe or PayPal takes 400 ms to respond, your customer waits 400 ms. If that API is having a bad afternoon, your checkout is having a bad afternoon.
It gets worse with several gateways active at once, since the calls often run sequentially rather than in parallel. Three gateways at 300 ms each is a full second before anything renders.
How to check: Install Query Monitor, open the checkout page, and look at the HTTP API Calls panel. It lists every outbound request with its duration. Anything above 200 ms on a page load is a problem; anything above 500 ms is the problem.
What to do:
Deactivate gateways you do not actually use. Most stores have at least one left over from a comparison they ran two years ago.
Check whether your gateway offers a caching or “deferred loading” option — the better ones do.
Where possible, move initialisation to the point of payment rather than page load.
Prefer gateways that load their SDK asynchronously via JavaScript over ones doing server-side calls on render.
This is the cause that most often requires a developer rather than a setting. It is also the one with the highest payoff, because it affects the final step before payment.
Cause 4: Cart and checkout pages incorrectly cached — or incorrectly excluded
Two opposite failures, both common.
Cached when they must not be. A misconfigured caching plugin, or a CDN with aggressive page rules, serves cart or checkout from cache. Symptoms are unmistakable: customers see someone else’s cart contents, the cart appears empty after adding a product, coupon codes do nothing, order totals are wrong. This is a data-leak-adjacent bug and takes priority over any performance work.
Excluded so broadly that nothing is cached. The more frequent version. Someone adds /cart, /checkout, /my-account — and then a wildcard rule, or an exclusion on the WooCommerce session cookie that ends up matching every visitor who has ever added a product. Now your entire store runs uncached for returning customers.
How to check: Open your caching plugin’s exclusion rules and read them literally, not by intention. Then test as a real customer would: add a product, browse to three unrelated pages, and check whether the cache header still indicates a hit. In DevTools → Network, look at the response headers of the HTML document — x-cache, cf-cache-status, or your plugin’s equivalent.
What you want: Cart, checkout and account pages excluded — specifically, by URL. Everything else cached. Cache bypass triggered only by an actual non-empty cart session, not by the mere presence of a WooCommerce cookie.
Object caching (Redis or Memcached) is what actually helps the uncached pages, because it caches database query results rather than whole pages. On a store where cart and checkout can never be page-cached, this is usually the single highest-impact infrastructure change available.
Measure first, in this order
Guessing is how stores end up with four performance plugins and a slower checkout than before. The diagnostic order:
Test the right URL. Run PageSpeed Insights against your checkout page, not your homepage — and do it with a product in the cart, in an incognito window. This alone tells most store owners something they did not know.
Read TTFB separately from the rest. Time to First Byte is server-side work: PHP, database, external calls. Everything after it is frontend: images, scripts, fonts. Slow TTFB and slow rendering need completely different fixes, and confusing them wastes the most time.
Install Query Monitor and load the checkout logged out with a cart. Read four panels: total query count, slowest queries, HTTP API calls, and PHP peak memory.
Run the autoload query above. It takes twenty seconds and it is the cheapest possible diagnostic.
Only then change something — one thing at a time, measured before and after.
Rough reference points for an uncached WooCommerce checkout on decent hosting: TTFB under 600 ms is acceptable, under 300 ms is good. Query count under 150 is reasonable, above 300 warrants investigation. These are orientation values, not targets — a store with complex tax rules and 40,000 products legitimately does more work than a shop selling twelve items.
What actually moves the number
In our experience, ordered by impact per hour invested:
Fix autoload bloat. Cheapest fix with the widest effect — it improves every uncached request on the site.
Add object caching (Redis). Most hosts support it; many stores simply never enabled it.
Remove cart fragments where they are pointless. Fast, low risk, immediately measurable.
Audit payment gateway calls. Highest impact on checkout specifically, but usually needs a developer.
Fix the caching exclusion rules. Often reveals that the store was barely being cached at all.
Reduce plugin count. Every active plugin loads on every uncached request. This is slow work, but it is the only fix that compounds.
What is mostly a waste of time
Worth saying, because these absorb enormous effort for little return on checkout:
Image optimisation on the checkout page. There are barely any images on it. Do it for product pages, where it genuinely matters.
Critical CSS and render-blocking work when your TTFB is 1.5 seconds. You are optimising the frontend of a slow backend. Fix the order.
Stacking performance plugins. Two caching plugins do not cache twice. They conflict, and diagnosing the result takes longer than the original problem would have.
Switching hosts as a first move. Sometimes correct, often an expensive way to avoid the audit. A 4 MB autoload array is slow on every host.
Why this is worth the effort
Checkout is the one page where every visitor has already decided to buy. Slowness anywhere else on your store costs you attention. Slowness here costs you revenue that was, for a moment, already yours.
And unlike most conversion work, this is not guesswork or A/B testing. The bottleneck is measurable, the fix is verifiable, and the improvement is permanent.
Not sure which of the four it is?
We work with WooCommerce every day, and checkout performance audits are a routine part of it. We measure first — TTFB, query load, gateway calls, autoload size — then fix what the numbers point to, and show you the before-and-after.
WooCommerce development and optimisation →
Something already broken rather than just slow? WordPress repair and emergency fixes →





