March 5, 2026

WooCommerce Checkout Slow? 4 Real Causes and How to Fix Them

woocommerce performance checkout optimization hosting

Updated August 30, 2026

The Page You Can’t Cache

Every speed optimization guide starts the same way: install a caching plugin, enable a CDN, compress your images. And for 90% of your pages, that works. Homepage? Cached. Category pages? Cached. Product pages? Mostly cached.

The short answer: a WooCommerce checkout is slow because it’s one of the few pages that can’t be cached (cart and My Account are the others) — every request runs the full PHP stack at bare server speed. The latency comes from four layers: server CPU and OPcache, the database (autoloaded options, postmeta bloat), blocking external APIs (Stripe, shipping, tax), and frontend JavaScript. Measure each layer, find the dominant one, and fix that first.

But your checkout page is different.

Checkout contains user-specific data — cart contents, shipping address, tax calculations, payment tokens. No caching plugin can touch it. Every single checkout load runs the full PHP execution pipeline, hits the database, calls external APIs, and renders the result from scratch.

Your checkout page runs at bare server speed. Always.

That’s why a store can score 95 on Lighthouse and still have an 8-second checkout. The cache hides the problem everywhere else. Checkout exposes it.

You’ve usually felt it earlier in the funnel, too: the moment a customer clicks Add to cart is the same kind of request — an uncacheable AJAX call that boots your whole stack — and it shows the same bare server speed.

And here’s the business case: Google and Deloitte’s “Milliseconds Make Millions” study found that a 100ms improvement in page load time produces an 8.4% increase in retail conversions. On checkout — the page where purchase decisions happen — every millisecond of latency is directly correlated with lost revenue.


How Bad Is It? Let’s Look at the Numbers

One r/woocommerce user ran a clean test: fresh WordPress install, default Twenty Twenty-Four theme, all plugins deactivated except WooCommerce itself. The wc-ajax=update_order_review call — the AJAX request that fires every time the checkout page updates — took 700-1100ms.

That’s nearly a full second of server response time with zero plugins, zero customization, and a stock theme. The overhead is coming from WooCommerce itself: stock checks, shipping calculations, tax logic, payment gateway validation. All of it runs on every checkout page load — even if you don’t use half of those features.

Now add what a real store looks like:

LayerLatency addedSource
WooCommerce core checkout logic700-1100msReddit benchmark, minimal setup
Stripe payment gateway+1000-2500msGitHub issue #1261, 3.5x slowdown vs. cash-on-delivery
Real-time shipping rates (USPS/UPS API)+200-800msExternal API round-trip
Tax calculation (Avalara)+100-500msExternal API round-trip
reCAPTCHA validation+100-300msGoogle API round-trip
Order Attribution Tracking+50-200msWooCommerce default since 8.5

Add those up. A “normal” WooCommerce checkout with Stripe, one shipping API, and a tax service is doing 2-5 seconds of server-side processing before the browser even starts rendering the response. And this is before we talk about frontend JavaScript.

One developer on r/woocommerce summed it up: “What’s the #1 thing that slows down your WooCommerce store?” — The top answer, with the most upvotes: “It’s WooCommerce itself.”


The Four Layers of Checkout Latency

Most guides treat checkout slowness as a single problem. It’s not. There are four distinct layers, and you need to diagnose which one is killing you before you can fix it.

Layer 1: The Server

PHP is single-threaded. When a customer clicks “Place Order,” WooCommerce processes the entire checkout sequence — validate cart, calculate taxes, create order, call payment gateway — on one CPU core, in one line. It can’t split this across multiple cores. It can’t parallelize.

This means the clock speed of that single core determines your checkout speed. A server with 8 cores at 2.3 GHz will process checkout slower than a server with 2 cores at 5.0 GHz. More cores handle more concurrent requests — but each individual checkout runs only as fast as one core can go.

Most managed hosting providers run older server-grade processors (Intel Xeon, AMD EPYC) at 2.0-2.5 GHz. These are designed for density — packing thousands of tenants onto shared hardware — not for single-threaded speed. A high-frequency AMD Ryzen 9 at 5.0+ GHz processes the same PHP code roughly twice as fast per request. That’s the difference between a 1.5-second checkout and a 0.75-second checkout. Physics, not software.

How to check your CPU: SSH into your server and run:

lscpu | grep "Model name\|MHz"

If you see “AMD EPYC 7xx1” at 2.0 GHz on a shared host — that’s a hardware ceiling no plugin can overcome.

OPcache is critical for checkout. OPcache stores pre-compiled PHP bytecode in memory so PHP doesn’t recompile scripts on every request. On cached pages, this barely matters — the page is served from HTML cache anyway. On checkout, it matters enormously, because every request runs the full PHP stack. We measured what an undersized OPcache does to a WooCommerce request, file by file, in 1,200 recompiles per page view — checkout is exactly the page where that cost lands.

One Reddit user discovered that two identical WooCommerce stores on the same hosting had drastically different checkout speeds. The difference? OPcache wasn’t enabled after a PHP version upgrade on the slower one. “This made such a big difference on AJAX functions (eg add to cart etc).”

Recommended minimum:

opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60

Layer 2: The Database

Every checkout triggers dozens of database queries. The two biggest offenders:

The autoloaded options problem. On every page load (including checkout), WordPress runs:

SELECT option_name, option_value
FROM wp_options
WHERE autoload = 'yes'

This loads every autoloaded option into memory. On a fresh WordPress install, that’s a few hundred KB. On a mature WooCommerce store with 30+ plugins, it can exceed 10MB. And here’s the real problem: the autoload column has no index by default. On a wp_options table with 50,000+ rows, this single query can take hundreds of milliseconds.

Check your autoload size:

SELECT SUM(LENGTH(option_value)) AS autoload_bytes
FROM wp_options
WHERE autoload = 'yes';

If it’s over 1MB, you have a problem. Over 5MB, it’s an emergency.

The postmeta bloat problem (legacy order storage). If you’re still using WooCommerce’s legacy order storage, every order creates 1 row in wp_posts and ~29 rows in wp_postmeta. A store with 50,000 orders has over 1.4 million rows in postmeta. The JOIN queries that WooCommerce runs during checkout slow to a crawl.

The fix: migrate to High-Performance Order Storage (HPOS). HPOS replaces the wp_posts/wp_postmeta model with optimized dedicated tables. WooCommerce’s own benchmarks show:

  • 5x faster order creation
  • 1.5x faster checkout throughput under concurrent load
  • 40x faster admin order filtering

If you haven’t migrated yet, this is likely your single biggest database bottleneck.

Layer 3: External APIs (The Silent Killer)

This is the layer most optimization guides miss entirely.

During a typical checkout, WooCommerce makes multiple blocking HTTP calls to external services:

  1. Payment gateway — Stripe processes the charge (1-3 seconds round-trip)
  2. Shipping rates — USPS/UPS/FedEx APIs return real-time shipping options (200-800ms)
  3. Tax calculation — TaxJar or Avalara calculates tax in real-time (100-500ms)
  4. Address validation — Some setups validate the shipping address against external databases
  5. Fraud prevention — reCAPTCHA or similar services verify the customer

Each of these is a synchronous HTTP call. PHP waits for each response before continuing. They don’t run in parallel — they stack.

The compounding is brutal. Take a checkout with Stripe + USPS real-time rates + Avalara tax + reCAPTCHA:

Stripe API:     ████████████████ 1800ms
USPS API:       ██████████       500ms
Avalara API:    ████████         400ms
reCAPTCHA:      ████             200ms
                                 ─────
Total blocking:                  2900ms

That’s nearly 3 seconds of waiting — and none of it is your server’s fault. Your PHP code is literally idle, waiting for external services to respond. No amount of server optimization will fix this. You need to either reduce the number of external calls or find ways to make them non-blocking.

How to find hidden API calls: Install Query Monitor and check the HTTP API Calls panel on your checkout page. You may discover plugins making calls to dead endpoints — one developer found that old, forgotten plugins were making API calls to services that no longer existed, and the timeout on those calls was adding 5-10 seconds per request.

Layer 4: The Frontend

After the server finishes processing, the browser still needs to render the checkout page. This is where payment gateway JavaScript creates problems.

Stripe’s JavaScript overhead: The official Stripe plugin loads 3 stylesheets globally, injects scripts on product pages (not just checkout), creates an analytics iframe, and sets cookies — all without regard for consent APIs. One developer documented that after simply enabling the Stripe plugin, the checkout JS payload increased significantly and the payment buttons took 10-15 seconds to render on a budget host.

The checkout block paradox: WooCommerce’s newer block-based checkout loads approximately 300 KB of compressed JavaScript — roughly 3x larger than the classic shortcode checkout. The request count nearly doubles. However, WooCommerce’s own data shows a 27% increase in checkout conversions from the block checkout due to better UX. Faster isn’t always better if the UX is worse.

Theme overhead on checkout: Multipurpose themes (Flatsome, Woodmart, Divi) load their entire CSS/JS stack on every page — including checkout. Sliders, animations, megamenu scripts, font libraries. None of it is needed on checkout. One developer reported that switching from a commercial theme to a custom lightweight theme cut their checkout page load from 6 seconds to 2.8 seconds.


How to Diagnose Why Your WooCommerce Checkout Is Slow

Don’t guess. Measure — and measure in layers, from the bottom up, so that each number isolates one thing. A single “checkout TTFB” tells you checkout is slow. It doesn’t tell you who is slow: the server, your plugin stack, or checkout itself.

Three rules before you start:

  1. Measure server time, not page load. Everything below is time spent generating the response. Frontend rendering is a separate layer (Layer 4) with separate tools.
  2. Repeat every measurement five times and take the median. The first request after a deploy hits a cold OPcache and an empty object cache; a single outlier proves nothing.
  3. Bypass the page cache. Checkout is never cached, but your baseline pages are. Being logged in as an admin bypasses page cache on every mainstream cache plugin — and it’s what lets Query Monitor run. (An admin request carries extra overhead a customer never sees — the admin bar, Query Monitor itself, plugins doing admin-only work. It’s real, but it’s constant across Steps 2–4, so it cancels out in the differences. We ignore it.) Do all of this on a staging copy that’s functionally identical but externally dead if you can; profiling tools on a live checkout are a risk you don’t need.

Two instruments: curl for what happens before WordPress, Query Monitor for what happens inside it. Query Monitor’s Overview panel gives you page generation time, database query time and query count; its HTTP API Calls panel lists every external request with its duration — all for the exact request you’re looking at, unaffected by any cache.

Step 1: The infrastructure floor — PHP without WordPress

Create hello.php in your web root:

<?php echo microtime(true);

Measure it from the server itself, so network latency doesn’t pollute the number:

for i in 1 2 3 4 5; do
  curl -s --resolve your-store.com:443:127.0.0.1 \
    -w ' pre=%{time_pretransfer} start=%{time_starttransfer}\n' https://your-store.com/hello.php
done

Two details that matter. --resolve points the request at your own nginx instead of going out through Cloudflare or a load balancer and back. And the number you want is start minus pre: pre is the TLS handshake with your own server, start is when the first byte arrived — the difference is web server + PHP-FPM and nothing else. On our test box the raw TTFB was 30 ms, of which 29 ms was the handshake and 1 ms was PHP. Print the body too: the script echoes the current timestamp, so if the same value comes back twice, you’re reading a cache, not PHP.

Expect single-digit milliseconds. Tens of milliseconds means PHP-FPM is queueing or the host is overloaded; hundreds means your problem is infrastructure and nothing you do inside WordPress will fix it. Every number below sits on top of this floor. Delete the file when you’re done.

No SSH? Measure from the browser’s DevTools Network tab instead and accept that the number includes the round trip to you. It still works as a floor, as long as you take Steps 2–4 from the same place.

Step 2: The WordPress floor — your plugin and theme stack

Open an empty page — a blank draft page, or your privacy policy — while logged in, and read Query Monitor: page generation time, database query time, number of queries, and whether the HTTP API Calls panel is empty.

This request boots WordPress, loads every active plugin, initialises your theme and reads all autoloaded options — but runs no checkout logic. Whatever it costs, checkout inherits.

Empty page generation timeWhat it tells you
< 150 msClean stack. Checkout slowness is checkout-specific — go to Step 3.
150–400 msTypical for a store with 30+ plugins. Note the number; it’s your baseline.
> 400 msYour stack is slow before WooCommerce does anything. Checkout will never be fast until this is.

If it’s slow, Query Monitor already tells you which layer it is:

  • Database query time dominatesLayer 2: autoloaded options, plugins querying on every page, duplicate queries.
  • HTTP API calls on an empty pageLayer 3: license checks, analytics pings, plugins phoning home on every request.
  • Neither — the time is just PHP → CPU-bound code: too many plugins, a heavy theme, or a slow core with OPcache off. That’s Layer 1, in two flavours — “too much code” and “too slow a CPU” — and only a PHP profiler (Code Profiler, wp profile) tells them apart. Check lscpu and OPcache status first; they’re free.

Step 3: The checkout page — what WooCommerce adds

Add a product to the cart, open /checkout/, read the same numbers. Subtract Step 2. The difference is the cost of checkout itself: session and cart totals, shipping and tax calculation, payment gateway initialisation, the checkout template.

Checkout minus empty pageDiagnosis
< 300 msCheckout logic is lean. If checkout still feels slow, it’s Step 4 or the frontend.
300–800 msNormal for stock WooCommerce plus one payment gateway.
> 800 msSomething checkout-specific is expensive — the panels below say what.

One caveat: with the block checkout, the page itself is a thin shell rendered in the browser, so expect a small delta here — the real work moves to the Store API requests in Step 4. With the classic shortcode checkout, the page does the work and the delta shows it.

Then look at where the extra time went:

  • HTTP API calls appear, or their total growsLayer 3. Shipping-rate APIs, tax services, gateway handshakes, anti-fraud. The panel lists each call with its duration — sort by time.
  • Database query time jumps and the new queries come from orders, postmeta or _wc_session_Layer 2. Legacy order storage or session bloat; HPOS is the fix.
  • Neither — PHP time grows and Queries by Component points at one plugin → that plugin is doing heavy work on checkout: dynamic pricing, discount rules, address validation. Deactivate it on staging, re-measure, confirm.

Step 4: The update_order_review request — what the customer feels

The checkout page loads once; the recalculation request fires every time the customer changes a field. Which request depends on your checkout:

  • Classic (shortcode) checkout: wc-ajax=update_order_review.
  • Block checkout: the Store API — POST /wc/store/v1/batch (and cart/select-shipping-rate, cart/update-customer), fired by api-fetch. Filter the Network tab by wc/store.

Change the shipping method five times and note the Waiting (TTFB) of each response.

This request recalculates totals, shipping and tax — no template, no theme. Compare it with the checkout page time from Step 3:

  • Close to the page time → the checkout logic is the cost (shipping and tax APIs, cart calculations), not rendering. Layer 3, or the plugin you found in Step 3.
  • Far below it → rendering is what’s slow: theme templates, the checkout block, hooks firing on woocommerce_checkout_*. That’s your theme or a front-end-heavy plugin.

Stock WooCommerce with no plugins already spends 700–1100 ms here (the benchmark at the top of this article), so treat anything under 1 s as normal, 1–2 s as worth fixing, and over 2 s as an emergency.

Reading the numbers together: a real store

Measured on 30 August 2026 on loftlight.pl, a WooCommerce store we look after. Setup: dedicated Hetzner server, Intel Core i5-13500 (up to 4.8 GHz), GridPane, nginx + PHP-FPM 8.3 (pm.max_children = 30), OPcache on (2 GB, 131k files), behind Cloudflare. WordPress 7.1, WooCommerce 11.0.1, 37 active plugins, Flatsome child theme, HPOS enabled, block checkout. Step 1 via curl from the server to localhost; Steps 2–3 logged in as admin with Query Monitor; Step 4 from DevTools. Five samples each — median shown, all samples in brackets.

StepMedianSamples
1. hello.php, server time (starttransfer − pretransfer)1.1 ms1.1 / 1.4 / 0.9 / 1.1 / 1.0
    …of which local TLS handshake, not PHP29 ms
2. Empty page — generation time204 ms181 / 204 / 232 / 193 / 274
    database time19 ms12 / 19 / 39 / 25 / 14
    queries151139 / 151 / 156 / 146 / 151
3. Checkout page (/zamowienie/) — generation time223 ms215 / 219 / 376 / 223 / 229
    database time18 ms12 / 18 / 40 / 14 / 19
    queries159159 / 154 / 159 / 152 / 159
4. Shipping method change → Store API batch (block checkout)276 ms276 / 217 / 316 / 310 / 236

Query Monitor’s HTTP API Calls panel was empty on both the empty page and the checkout — “No HTTP API calls” on either request. Nothing in the loop phones home, and there’s no Stripe in the stack.

Bottom-up:

  • Infrastructure: 1 ms. The floor is effectively zero. Note the trap: raw TTFB from curl was 30 ms, and 29 of those were the TLS handshake with our own nginx. Measure the difference, or you’ll blame PHP for your certificate.
  • WordPress stack: ~200 ms. 37 plugins, 151 queries — but only 19 ms of database time. The remaining ~180 ms is PHP: plugin initialisation, the theme, autoloaded options being unserialised. That is the single biggest fixed cost on this store, and it’s paid on every uncached request. It’s also CPU time on a 4.8 GHz core; on a 2.0–2.5 GHz shared host the same code would cost roughly twice as much before WooCommerce did anything.
  • Checkout: +19 ms, +8 queries, zero HTTP calls. Almost nothing — because this is block checkout, and because nothing in the loop phones home. The page is a shell; the work moves to the Store API, which is Step 4.
  • Shipping recalculation: 276 ms, including an ~80 KB JSON response. The entire checkout interaction the customer feels is under 300 ms. Compare that with the 700–1100 ms a stock, plugin-free WooCommerce spent on the same job in the Reddit benchmark at the top of this article: same WooCommerce code, different hardware. Physics.

Verdict: nothing to fix on Layers 1–3. The only meaningful lever left is the ~180 ms of plugin PHP in every request — the “too much code” flavour of Layer 1, which is profiler territory, and a nice-to-have at this speed. If this store ever “felt slow” at checkout, the next place to look would be Layer 4 — Flatsome plus block-checkout JavaScript — not the server.


How to Fix a Slow WooCommerce Checkout (Layer by Layer)

Based on the diagnosis above, here’s what to fix — in order of impact. None of it is “install a caching plugin.”

If your TTFB is over 1 second: fix the server first

No plugin can overcome slow hardware. If your server runs at 2.0 GHz on shared infrastructure, the checkout will be slow regardless of what you optimize. The single highest-impact change is moving to dedicated high-frequency CPU — which is why we argue hosting should be step one, not the last resort. We’ve benchmarked WooCommerce checkouts on AMD Ryzen 9 (5.0+ GHz) vs standard Xeon hosts — the difference is typically 40-60% faster server response time, which translates directly to checkout speed.

If Query Monitor shows 200+ database queries: fix the database

  1. Migrate to HPOS if you haven’t. 5x faster order creation is not a marginal improvement.
  2. Audit autoloaded options. Disable autoload on everything that isn’t needed on every page.
  3. Clean orphaned data. _wc_session_ entries, transients, revision postmeta — these accumulate silently.

If external API calls dominate: reduce blocking calls

  1. Use flat-rate or table-rate shipping instead of real-time carrier APIs where possible. Real-time rates look nice but add 200-800ms per checkout. If your shipping is predictable, pre-calculate it.
  2. Evaluate your tax solution. TaxJar responds in under 20ms. Avalara can take hundreds of milliseconds. If you’re using Avalara, consider whether the additional complexity is worth the latency.
  3. Audit payment gateway plugins. Run checkout with each gateway individually and compare timing. Stripe’s documented 3.5x overhead is gateway-specific — alternatives may be faster for your use case.
  4. Remove dead endpoints. Query Monitor’s HTTP API panel reveals plugins calling APIs that no longer exist. The timeout on those calls can add 5-30 seconds.

If frontend JavaScript is the bottleneck: audit what loads on checkout

  1. Disable plugins per page. Tools like Asset CleanUp or Perfmatters let you prevent non-essential plugins from loading CSS/JS on checkout. Your countdown timer, your popup plugin, your slider — none of them belong on checkout.
  2. Evaluate the checkout block vs shortcode. Block checkout is heavier (3x JS) but converts 27% better. Test both and measure conversion rate, not just page speed.

The Uncomfortable Truth

Most WooCommerce speed guides are written by hosting companies or plugin vendors. Their advice naturally gravitates toward what they sell — “upgrade your hosting plan” or “install our optimization plugin.”

The reality is more nuanced. Checkout latency is a compounding problem across four distinct layers, and the fix depends entirely on which layer is your bottleneck. A caching plugin won’t help because checkout can’t be cached. A faster CDN won’t help because the latency is server-side. And “disabling plugins” is a blunt instrument that often breaks functionality you need.

The approach that actually works: measure each layer independently, identify the dominant bottleneck, and fix that first. Then move to the next layer.

Every update we deploy through WooCommerce Care goes through this exact diagnostic process. We profile the checkout, identify where the milliseconds are hiding, and eliminate them layer by layer — server tuning, database optimization, API audit, frontend cleanup. Then we verify with automated Playwright tests that the entire purchase flow still works end-to-end (here’s how to write your first one).

If your checkout takes more than 2 seconds and you’re not sure where the latency lives — that’s exactly what we diagnose.


FAQ: Slow WooCommerce Checkout

Why is my WooCommerce site slow only during checkout?

Because checkout is one of the pages your cache can’t serve — cart and My Account are the others — and it’s the one where money changes hands. Homepage, category and product pages come from static HTML cache; checkout holds user-specific data (cart, address, taxes, payment tokens), so every load runs PHP, the database and external payment, shipping and tax APIs from scratch. Your site isn’t fast — your cache is. Checkout is where you see the real server speed.

How do I fix a slow WooCommerce checkout page?

Measure in layers before you touch anything: a bare PHP file (infrastructure), an empty page (your plugin stack) and the checkout page (what WooCommerce adds), each with Query Monitor open and repeated five times. Then fix the dominant layer: the server (CPU clock speed, OPcache) if TTFB is over 1 second; the database (HPOS, autoloaded options) if you see 200+ queries; external APIs (real-time shipping, tax, dead endpoints) if HTTP calls dominate; the frontend (theme and plugin assets) if the server is fast but rendering is slow.

Why is WooCommerce checkout very slow with Stripe?

The official Stripe gateway adds a synchronous API round-trip to every order — documented at roughly 3.5x the time of cash-on-delivery in GitHub issue #1261 — and loads its scripts, stylesheets and an analytics iframe on the frontend. On a slow host that can mean payment buttons taking 10+ seconds to render. Test checkout with each gateway individually and compare the wc-ajax=checkout timing.

Is WooCommerce add to cart slow for the same reason?

Yes. Add to cart is a POST to wc-ajax=add_to_cart (or the Store API on block themes) that WooCommerce sends with no-cache, no-store headers: it boots the whole plugin stack, validates the product, writes the session and renders the mini-cart, and no page cache can serve it. It’s the first uncached request most customers ever make, so it’s usually where they first feel the server. We break that request down step by step in WooCommerce Add to Cart Slow? Anatomy of the Request No Cache Can Save.

Does a caching plugin speed up WooCommerce checkout?

No. Caching plugins store rendered HTML for anonymous visitors; checkout is dynamic and excluded from page cache by design. What helps on checkout is OPcache (bytecode cache), an object cache like Redis for repeated database reads, and fewer blocking API calls — not page caching.


Checkout Speed Diagnostic Checklist

Before optimizing, measure — in this order:

  • hello.php TTFB from the server (median of 5) — infrastructure floor
  • Empty page, logged in, Query Monitor: generation time, query count, DB time, HTTP API calls — WordPress floor
  • Checkout page, the same four numbers — and the delta over the empty page
  • wc-ajax=update_order_review TTFB (Network tab, 5 samples) — and how it compares to the checkout page time
  • Autoloaded data size (SQL query above)
  • CPU model and clock speed (lscpu)
  • OPcache status (php -i | grep opcache)
  • HPOS migration status (WooCommerce > Settings > Advanced > Features)
  • JavaScript payload size on the checkout page (Layer 4 — separate tools)
M
Written by

Mateusz Zadorozny

SHIFT64 Founder. WooCommerce performance specialist helping store owners achieve faster load times and better conversions.