WooCommerce Checkout Slow? 12 Real Fixes That Cut Page Load by 60%+ (2026)
If your WooCommerce checkout takes 4+ seconds to respond — or worse, freezes on shipping or payment updates — the cause is almost always one of a dozen well-known problems. This is a diagnostic guide, not a "install a caching plugin" thread. Each fix tells you what to measure, what to change, and when it actually helps.
14-second narrated walkthrough: slow checkout → SimpleReview → PR with a fix
Key Takeaways
- WooCommerce checkout is slow on 90%+ of stores because of plugin hooks, not core WooCommerce.
- Cart fragments (
wc-cart-fragments.js) fire AJAX on every page — disable them everywhere except/cart/and/checkout/. - Autoloaded options above 1 MB slow every request. Most stores ship 2–5 MB after a year of plugin churn.
- The
update_order_reviewAJAX re-runs all checkout hooks on every field change. Profile it with Query Monitor — if it takes over 1 second, one plugin is guilty. - PHP 8.1+ plus object caching (Redis) gives a 30–50% checkout speedup before any code changes.
- Page caching does not help checkout — WooCommerce already excludes it — but the rest of the site should be cached so checkout is the only backend-heavy page.
- Heartbeat API, Google Fonts inlined, and payment gateway JS (especially PayPal Smart Buttons) are the top three front-end offenders.
Fix 1: Profile First, Then Change Code
Before touching anything, install Query Monitor (free). Load /checkout/ and expand "Hooks & Actions" and "Queries". You are looking for three numbers: total queries, slowest query, and plugins attached to woocommerce_checkout_update_order_review, woocommerce_checkout_process, and woocommerce_after_calculate_totals.
Healthy checkout: under 80 queries, no query over 200 ms, under 10 hooks per checkout action. If you see 400+ queries or a single query above 1 second, you have a concrete target.
Fix 2: Kill Cart Fragments Sitewide (Except Cart/Checkout)
The single biggest front-end win. WooCommerce loads wc-cart-fragments.js on every page to refresh the mini-cart count via AJAX. On pages with no cart widget, it is pure waste — and it blocks admin-ajax.php.
// functions.php
add_action( 'wp_enqueue_scripts', function() {
if ( is_cart() || is_checkout() ) return;
wp_dequeue_script( 'wc-cart-fragments' );
}, 11 );
Real-world impact: 200–600 ms off every non-cart page, including category and product pages where users browse before checkout.
Fix 3: Shrink Autoloaded Options
Every WordPress request loads all rows from wp_options with autoload = 'yes'. Plugins dump session data, logs, and transients there and never clean up. Over a year this balloons.
Check your total:
SELECT SUM(LENGTH(option_value))/1024/1024 AS autoload_mb
FROM wp_options WHERE autoload = 'yes';
Anything over 1 MB is a problem. Above 3 MB and every request — including checkout — pays the tax. Find the biggest offenders:
SELECT option_name, LENGTH(option_value)/1024 AS kb
FROM wp_options WHERE autoload = 'yes'
ORDER BY LENGTH(option_value) DESC LIMIT 20;
Typical culprits: abandoned plugins, action_scheduler_ rows (switch to a cleanup cron), _transient_ that should not be autoloaded, session handlers from plugins that never clean up.
Fix 4: Object Caching (Redis or Memcached)
WooCommerce is query-heavy. Without object cache, every checkout AJAX call re-runs dozens of queries that did not change. With Redis, the second update_order_review tick drops from 1.5 s to 200 ms on most stores.
If you are on managed WooCommerce hosting (Kinsta, WP Engine, SpinupWP), object cache is usually one click. On a VPS, install Redis and the redis-cache plugin. Verify with Query Monitor: you should see most non-persistent queries disappear after the first load.
Fix 5: Audit Plugins Hooked Into Checkout
Every plugin that attaches to woocommerce_checkout_update_order_review runs on every field change. A single plugin making a remote API call there can turn a 400 ms checkout into a 3 s one.
Find them:
// functions.php — log what runs on update_order_review
add_action( 'woocommerce_checkout_update_order_review', function( $post_data ) {
global $wp_filter;
$hooks = $wp_filter['woocommerce_checkout_update_order_review'] ?? null;
if ( $hooks ) {
error_log( 'checkout hooks: ' . print_r( array_keys( (array) $hooks->callbacks ), true ) );
}
}, 1 );
The worst offenders historically: tax-sync plugins (Avalara, TaxJar in aggressive mode), shipping quote plugins that hit carrier APIs on every address change, loyalty/points plugins that recalculate on every tick, currency converters that call remote FX APIs.
Fix 6: PHP 8.1+ and OPcache
If you are still on PHP 7.4, you are leaving 20–40% performance on the table. WooCommerce officially supports PHP 8.1+ as of WooCommerce 9.0. OPcache should be on with opcache.memory_consumption=256 and opcache.max_accelerated_files=10000.
Fix 7: Disable or Throttle the Heartbeat API
The Heartbeat API polls admin-ajax.php every 15–60 seconds. On the checkout page, admin users sometimes have it firing at 15 s and stacking with update_order_review ticks. Throttle it or disable it on the front end:
add_action( 'init', function() {
wp_deregister_script( 'heartbeat' );
}, 1 );
Keep it on the admin dashboard for post locking, but there is no reason for it on /checkout/.
Fix 8: Payment Gateway JS Bloat
PayPal Smart Buttons, Klarna, Afterpay, and Amazon Pay each load 100–300 KB of third-party JS, often synchronously. If you have all four enabled and the customer only ever uses Stripe, you are paying the full cost on every checkout.
Audit what the checkout actually loads in Chrome DevTools → Network → JS, sorted by Size. If a gateway JS loads but nobody uses it, disable the gateway or defer its script until the user selects it.
Fix 9: Remove Unused Checkout Fields
WooCommerce ships with 15 billing fields and 11 shipping fields. Every field you keep is work — validation, AJAX refresh, hook execution. If you sell a digital product and do not need shipping, disable shipping fields entirely. The woocommerce_checkout_fields filter lets you remove them:
add_filter( 'woocommerce_checkout_fields', function( $fields ) {
unset( $fields['billing']['billing_company'] );
unset( $fields['billing']['billing_address_2'] );
unset( $fields['order']['order_comments'] );
return $fields;
} );
Fix 10: Block Checkout Caching (Correctly)
WooCommerce already sets DONOTCACHEPAGE on /cart/, /checkout/, and /my-account/. Most caching plugins respect this. But a misconfigured caching plugin can still cache these pages, breaking nonces and session data, which appears to the user as "checkout hangs".
Verify: open the checkout in an incognito window, check response headers for X-Cache: HIT or Cloudflare's cf-cache-status. Should be MISS or DYNAMIC. If it is HIT, your cache is breaking checkout.
Fix 11: Defer Google Fonts and Third-Party Scripts
Most themes load 3–5 Google Font families with blocking <link> tags. Self-host them with font-display: swap or defer them. Move analytics, tag manager, chat widgets, A/B test libraries to async or load after checkout is interactive.
Rule: on /checkout/, nothing non-essential should block the main thread in the first 2 seconds.
Fix 12: Review Custom Code — Especially AI-Generated Snippets
The single most common cause of "checkout got slow last week" is a custom snippet. AI assistants are very good at generating working WooCommerce code and fairly bad at understanding which hooks run on every AJAX tick. A snippet that "just" calls an external API on woocommerce_checkout_process looks fine in isolation and kills performance in production.
If you shipped custom code recently — from ChatGPT, Claude, Cursor, Copilot, or a human — and the checkout degraded after, that is almost certainly the cause. Roll back the snippet, confirm the slowdown goes away, then decide whether to rewrite it correctly.
Shipped AI-generated WooCommerce code and checkout got slow?
SimpleReview reads your repo, runs the checkout under profiling, finds the exact hook or query causing the slowdown, and auto-deploys the fix. First fix is free.
Get a code review →Which Fix Should You Start With?
| Symptom | Most likely cause | Fix to try first |
|---|---|---|
| Whole site is slow, not just checkout | Autoload bloat, no object cache, PHP 7.x | Fixes 3, 4, 6 |
| Checkout slow, shop pages fast | Plugin hooks on checkout actions | Fixes 1, 5, 12 |
| Address change freezes page for seconds | update_order_review hooked by a tax/shipping plugin | Fix 5 |
| First load fine, subsequent loads slow | No object cache, Heartbeat stacking | Fixes 4, 7 |
| Checkout stalls with spinner forever | Page caching plugin caching /checkout/ | Fix 10 |
| Got slow after adding a custom snippet | Custom/AI-generated hook misuse | Fix 12 (roll back, then review) |
| Heavy on mobile, OK on desktop | Payment gateway JS + Google Fonts | Fixes 8, 11 |
Fast Diagnostic Checklist (15 minutes)
- Open
/checkout/in Chrome DevTools → Network. Note the total load time and the largest request. - Install Query Monitor → check total queries and slowest query on checkout load.
- Run the
autoload_mbSQL query. If over 1 MB, start there. - Check PHP version (
<?php phpinfo(); ?>). If under 8.1, upgrade before anything else. - Add an address in the checkout, watch the
update_order_reviewAJAX request in Network. If over 1 s, a plugin is the cause. - Disable non-essential plugins one by one (or use Health Check plugin's troubleshooting mode) to isolate.
- If recent custom code — roll it back and retest.
FAQ
woocommerce_checkout_update_order_review making a slow remote call. Disable cart fragments sitewide except on cart/checkout, audit autoload, and profile with Query Monitor./cart/ and /checkout/. A one-line dequeue saves 200–600 ms on every other page.woocommerce_checkout_process or update_order_review without realizing those fire on every AJAX tick. If the slowdown started right after a new custom snippet, roll it back first.