# WooCommerce checkout slow? Find the cause: server, gateways, shipping or emails

> A slow WooCommerce checkout has four usual causes: an uncached page on a slow server, payment gateway scripts, live shipping and tax lookups, and work done while the order is placed. Here is how to time each one and fix it.

Updated 2026-09-26 · WordPress & WooCommerce · HTML version: https://getreport.app/guides/woocommerce-checkout-slow

A WooCommerce checkout is slow for one of four reasons: the server takes too long to build a page that can never be cached, payment and tracking scripts weigh the page down, shipping and tax lookups call outside services every time the address changes, or placing the order waits for payment, stock, emails and extensions one after another. The fix depends on which of the three moments is slow: opening the checkout, updating the totals, or clicking Place order. This guide shows how to time each moment in ten minutes and what to change for each cause. It is part of the [WooCommerce store checklist](https://getreport.app/guides/woocommerce-store-checklist), where the checkout is step 5.

## Quick answer

- **Time three moments:** the checkout page loading, the totals updating after you change the address, and Place order until the thank-you page.
- **Slow page load:** measure Time to First Byte on the checkout. Over about 800 ms means the host, PHP version or missing object cache.
- **Slow totals update:** live shipping rates, tax services and address plugins that call outside APIs on every change.
- **Slow Place order:** the payment gateway's API, emails sent during the request, and extensions that sync stock or orders to other systems.
- **Heavy page:** disable payment methods and express buttons you do not use, and keep chat, heatmaps and A/B tests off the checkout.
- **Never cache the checkout** to make it faster; it breaks orders.

## How the checkout works, and where the time goes

Knowing what happens at each moment tells you where to look.

**1. Opening the checkout.** The page is built by PHP on every request, because it is different for every shopper. The server loads WordPress, WooCommerce and every active plugin, reads the cart from the session, calculates totals and shipping, and renders the form. The browser then loads the theme, WooCommerce's scripts and each payment method's scripts.

**2. Updating the totals.** When the shopper types an address or changes the country, WooCommerce recalculates shipping, tax and totals. The classic checkout (the `[woocommerce_checkout]` shortcode) sends a `?wc-ajax=update_order_review` request. The Checkout block, the default for new stores since WooCommerce 8.3, calls the Store API, for example `/wp-json/wc/store/v1/cart/update-customer` or a `/batch` request. Each call runs PHP again, plus any outside lookups for shipping rates or tax.

**3. Placing the order.** Clicking Place order sends the form to `?wc-ajax=checkout` (classic) or `/wp-json/wc/store/v1/checkout` (block). WooCommerce validates the form, creates the order, reduces stock, calls the payment gateway's API, sends the order emails and runs every extension hooked into the order, before the shopper sees the thank-you page.

## How to time a slow checkout

You need a browser and ten minutes. Test as a shopper would: logged out, in a private window.

1. Add a product to the basket and open the checkout.
2. Open the developer tools (F12) → Network, tick "Disable cache", and reload. Click the first request (the checkout page itself) and open Timing: **Waiting for server response** is its Time to First Byte.
3. Filter by "Fetch/XHR". Change the country or postcode and watch the `update_order_review` or Store API request appear. Its duration is the totals update.
4. Use your gateway's test mode and a test card, click Place order and time the `checkout` request until the thank-you page loads.
5. Sort the Network list by domain. Count the scripts from payment providers, tracking and other services.

For the server side, the free Query Monitor plugin shows, for each page and AJAX request, the slowest database queries, the plugins that made them, and the outgoing HTTP API calls with their duration. Those outgoing calls are often the answer: a carrier rate lookup of 2 seconds shows up there by name. Use it on staging, or on production for a short while, logged in as an administrator.

| What is slow | Usual cause | Where to look |
| --- | --- | --- |
| The page before anything shows | Server: hosting, PHP, no object cache | TTFB of the checkout request |
| The page after the HTML arrives | Gateway and third-party scripts | Network tab, sorted by domain |
| Totals after an address change | Shipping, tax or address APIs | Duration of the update request; Query Monitor's HTTP API calls |
| Place order | Gateway API, emails, order-sync extensions | Duration of the checkout request; Query Monitor |

## Cause 1: a slow server on an uncached page

Product and category pages can come from a page cache; the checkout never can. So the checkout shows your server's real speed. Google's web.dev guidance rates a Time to First Byte of 800 ms or less as good. A checkout that needs 1.5 to 3 seconds before anything happens has a server problem, not a theme problem.

What to change, in order of cost:

- **PHP version.** Run a version WordPress and WooCommerce currently support; WooCommerce recommends 8.3 or newer.
- **Persistent object cache.** Redis or Memcached, if the host offers it, keeps options, sessions and product data in memory between requests.
- **Plugins that load on every request.** Every active plugin runs on the checkout. Remove the ones you do not use; the report's plugin cost list shows the heavy ones on the front end.
- **The hosting plan.** If the plan is the limit, [WooCommerce hosting](https://getreport.app/guides/woocommerce-hosting) shows how to compare hosts by uncached TTFB on your own store.

> **Free tool:** [TTFB test: check your time to first byte](https://getreport.app/tools/ttfb-test): Free TTFB test: measure time to first byte from Frankfurt, in a Lighthouse run and from real Chrome users, plus the redirects before the page. No sign-up.

With an empty basket, the classic checkout redirects to the cart, so a TTFB test on `/checkout/` usually measures the cart. That is still an uncached WooCommerce page and a fair proxy. For the exact number, use the Network tab with a product in the basket.

> **Check: Time to First Byte.** TTFB is how long the server takes to start answering. Everything else waits for it, so a slow first byte makes every other metric worse. Google's 800 ms target is for real visitors, network included; Lighthouse flags the server's own share above 600 ms (see server response time).
>
> 1. Add page caching (WordPress: WP Rocket, LiteSpeed Cache, or your host's cache; Shopify does this for you).
> 2. Put a CDN in front (Cloudflare, Bunny) so the first byte comes from a nearby edge.
> 3. Check database-heavy plugins and slow hosting; a plan with more CPU often fixes this outright.

## Cause 2: payment gateway and third-party scripts

Each payment method loads its own scripts: a card-field library, a wallet SDK for Apple Pay, Google Pay or PayPal buttons, sometimes a fraud-detection script. Three gateways with express buttons can add more JavaScript than the rest of the page combined, and some load it on every page of the store, not only the checkout.

- **Disable payment methods you do not use** under WooCommerce → Settings → Payments. A method that is installed and enabled loads its scripts even if nobody picks it.
- **Limit express buttons** (Apple Pay, Google Pay, PayPal) to the places they earn their keep. Most gateways let you choose between product page, cart and checkout.
- **Check where gateway scripts load.** Open a blog post and look for the gateway's domain in the Network tab. If it loads there, look for a setting to restrict it, or ask the gateway's support.
- **Keep the checkout clean.** No chat widget, heatmap, A/B test or social pixel is worth a slower payment step; this also narrows the room for card-skimming scripts, as [WooCommerce security](https://getreport.app/guides/woocommerce-security) explains.

> **Check: Third-party code weight.** Tags, widgets and embeds from other companies run on your visitors' phones at your page's expense. Over 250 KB or 250 ms of blocking is a sign they dominate the load.
>
> 1. List what each third party gives you; remove the ones nobody looks at.
> 2. Load the rest after interaction or with a facade (a static thumbnail for YouTube, a click-to-load chat button).
> 3. Move tags into a tag manager that fires them late, not in <head>.

## Cause 3: shipping, tax and address lookups

Totals that take seconds to update after the shopper types a postcode almost always come from an outside call.

- **Live carrier rates.** Plugins that fetch rates from a carrier's API make a request each time the address or basket changes. WooCommerce keeps calculated rates in the session and only recalculates when the package changes, but a slow carrier API still adds its full response time. Use flat or table rates where you can, and set a short timeout in the plugin if it has one.
- **Many shipping methods and zones.** Every method in the matching zone is calculated. Remove methods and zones you no longer offer.
- **Tax services.** Automated tax plugins call their service to calculate tax for the address. Check their timeout and caching options.
- **Address autocomplete and validation.** They call an outside API as the shopper types. Keep one, not two.
- **"Hide shipping costs until an address is entered"**, under WooCommerce → Settings → Shipping → Shipping options, skips rate calculation before there is an address to calculate for.

In Query Monitor, the HTTP API calls panel on the update request shows each outside call and how long it took. The slowest one is your answer.

## Cause 4: slow Place order

The moment after Place order is the most stressful for a shopper, and it does the most work.

- **The gateway API.** Authorising the payment takes a round trip to the provider. It is usually well under a second; if it is not, check the gateway's status page and your server's connection to it.
- **Order emails.** By default, WooCommerce sends the new-order and confirmation emails while the shopper waits. Sending through a slow SMTP server can add seconds. Use a transactional email service with an API, or turn on "Deferred emails" under WooCommerce → Settings → Advanced → Features (WooCommerce 10.8 and later), which sends them in the background through the Action Scheduler.
- **Extensions that act on new orders.** Stock sync with a marketplace or warehouse, accounting exports, CRM and marketing tools often run on the order hook and call their own APIs. Look for a setting to run them in the background, or replace them with ones that do. WooCommerce's own webhooks are already delivered in the background by default.
- **Fraud checks** that call a scoring service during checkout. Keep one, and prefer the gateway's own.

If Place order is slow only sometimes, background jobs may be piling up. [Speeding up the WooCommerce backend](https://getreport.app/guides/woocommerce-backend-slow) covers the Action Scheduler queue and a real server cron.

## Cause 5: requests competing with the checkout

Other requests on the checkout page compete with it for the same PHP workers:

- **Cart fragments**, the request that refreshes the header basket, should run on the cart and checkout only if the theme needs it. On other pages it wastes a PHP worker on every view. [WooCommerce cart fragments](https://getreport.app/guides/woocommerce-cart-fragments-the-ajax-call-on-every-page) shows how to stop it.
- **admin-ajax requests** from plugins (live chat, popups, counters) and the WordPress Heartbeat, which runs every minute or so on pages where a plugin enables it.

On a small plan with few PHP workers, three of these at once make the checkout's own request wait in a queue.

> **Free tool:** [WooCommerce SEO and speed checker](https://getreport.app/tools/woocommerce-checker): Free WooCommerce SEO and speed check: product schema with price and stock, out-of-stock pages left indexable, cart fragments and a cached cart or checkout.

> **Check: No cart-fragments request on this page.** cart-fragments.js fires a wc-ajax=get_refreshed_fragments request to keep the mini-cart count fresh. Since WooCommerce 7.8 it loads only with the Mini-Cart widget or block; here it still loads on a page that is not the cart, often because a theme or plugin enqueues it. The request bypasses the page cache, so every visit costs a PHP request, and it delays the page for a feature most pages do not show.
>
> 1. Disable cart fragments on pages that are not the cart or checkout. WooCommerce has no setting for it: use the "Disable cart fragments" option in Perfmatters or a similar performance plugin, or a small snippet that dequeues wc-cart-fragments outside the cart, checkout and account pages.
> 2. If the header shows a live cart count, load the count from a lightweight endpoint on cart changes only.

## Do not cache the checkout to make it faster

A cached checkout is fast and broken: it serves one shopper's basket and security token to the next, and orders fail with "session expired" errors. The [WooCommerce checker](https://getreport.app/tools/woocommerce-checker) fetches `/cart/` and `/checkout/` as an anonymous visitor and reads the cache headers to catch exactly this.

> **Check: Cart and checkout bypass the page cache.** A cached /cart/ or /checkout/ shows one visitor another visitor's basket, breaks nonces and payment forms, and produces the "session expired" errors shoppers abandon on. Every caching plugin excludes these pages by default; a CDN or host cache in front of them often does not.
>
> 1. Exclude /cart/, /checkout/ and /my-account/ (and their translations) from every cache layer: the caching plugin, the host's page cache and the CDN (Cloudflare: a Cache Rule with "Bypass cache" for those paths and for requests with a woocommerce_* cookie).
> 2. Re-test after purging: the responses should carry cache-control: no-store or a cache status of BYPASS/MISS.

[Keeping cart, checkout and account pages out of the cache](https://getreport.app/guides/woocommerce-caching-cart-checkout-and-account-pages) shows the exclusion for each cache layer.

## Classic checkout or Checkout block?

Neither is always faster. The Checkout block loads a larger JavaScript bundle up front but updates totals through the Store API without reloading the order review. The classic checkout loads less JavaScript but re-renders the review table with each `update_order_review` call. Extensions matter more than the choice: a gateway or shipping plugin that does not fully support the block may fall back to slower behaviour. If you are thinking of switching, test both on a staging copy with your real gateways and shipping, using the timing steps above.

## Common mistakes

- **Measuring the home page.** It comes from the cache. The checkout is the page that shows the server's real speed.
- **Testing while logged in as an administrator.** Admin toolbars and plugins that load only for admins change the numbers. Use a private window.
- **Enabling every payment method "just in case".** Each one loads scripts and adds choices. Keep the ones your customers use.
- **A cache rule that includes the checkout.** It turns a slow checkout into a failing one.
- **Blaming WooCommerce core.** Profiling almost always finds an outside API call, a heavy extension or a small hosting plan.

## Questions people ask

### Why does my WooCommerce checkout take so long to load?

Because the checkout can never come from a page cache, so every visit runs WordPress, WooCommerce and all active plugins on your server. A slow host, an old PHP version or no object cache shows up there first. Measure its Time to First Byte in the browser's Network tab; over about 800 ms points to the server. Then check how many payment and tracking scripts the page loads.

### Why is the WooCommerce "Place order" button slow?

Placing an order creates the order, reduces stock, calls the payment gateway's API, sends order emails and runs every extension hooked to new orders, all before the thank-you page appears. Slow SMTP email and extensions that sync orders to other systems are the usual culprits. Send email through a transactional email API or turn on deferred emails, and move syncing extensions to the background.

### Why do WooCommerce checkout totals take so long to update?

Each change of address or country recalculates shipping, tax and totals on the server. Live carrier rates, tax services and address validation plugins call outside APIs during that request, and a slow API delays every update. Query Monitor's HTTP API calls panel shows which call is slow. Use flat or table rates where possible, and remove shipping methods and zones you no longer offer.

### Is the WooCommerce Checkout block faster than the classic checkout?

Not automatically. The Checkout block loads more JavaScript up front but updates totals through the Store API; the classic shortcode checkout loads less but reloads the order review on each change. Your gateways, shipping plugins and hosting usually matter more than the choice. Test both on a staging copy with your real extensions before switching.

### Can I cache the WooCommerce checkout page?

No. The checkout holds the shopper's basket, address and a security token that is valid only for their session. A cached checkout shows one shopper's details to the next and makes orders fail with "session expired" errors. Exclude the checkout, cart and my-account pages from every cache layer, including the CDN, and make the server fast instead.
