Skip to content
Blog

WooCommerce Checkout Stuck Loading? Common Causes and Fixes

November 12, 2026 · Nordic Digi Solutions · 15 min read

A WooCommerce checkout that keeps loading forever stops customers from placing orders while the rest of the site looks completely normal. The customer reaches checkout, enters their details, and the order review just keeps spinning. Sometimes the payment methods never appear, sometimes the Place order button does nothing, and sometimes nothing obvious happens at all.

The cause can sit in several layers: caching, plugin or theme conflicts, PHP warnings, server rewrites, security rules or broken JavaScript. So the fix is not to disable plugins at random or rebuild checkout, but to find where the checkout request breaks. This guide shows how.

What “WooCommerce Checkout Stuck Loading” Usually Means

Checkout is dynamic, and the requests it sends depend on the checkout type. To see which one you have, edit the page assigned as Checkout under WooCommerce → Settings → Advanced:

  • Classic checkout (a Classic Shortcode or Shortcode block) uses WooCommerce’s AJAX endpoints: /?wc-ajax=update_order_review when the page loads, an address field or the shipping method changes, or a coupon is applied or removed, and /?wc-ajax=checkout when the customer clicks Place order.
  • Checkout block, the default for new stores since WooCommerce 8.3, sends its cart and checkout requests to the Store API under /wp-json/wc/store/v1/.

On the classic checkout, the spinner is WooCommerce’s own loading overlay. Before sending update_order_review, the checkout script dims the order review and payment section, and it removes the overlay only when the response brings back fresh HTML for both. The request has no error handler, so a 403, a 500, an HTML page or broken JSON leaves the overlay up for good. The spinner is the symptom; the cause is whatever stopped a valid response: a request that never completes, an error, HTML instead of JSON, a cache or firewall answering instead of WordPress, PHP warnings in the response, or failing JavaScript.

Common Symptoms of a Broken WooCommerce Checkout

An endless spinner over the order review, payment methods that never appear, a Place order button that does nothing, a page that reloads without placing the order, shipping methods that never update, a blank order review box, console errors, or an order received page that is never reached. Customers often report that “payment is not working”, but checkout can break before the payment step even begins.

First Rule: Do Not Assume It Is the Payment Gateway

The payment plugin gets blamed first. But if the spinner appears while totals load, before anyone clicks Place order, no gateway has been asked to do anything yet. WooCommerce’s own guide treats a stall after Place order as a separate case, with the gateway, fraud and tax services, webhooks and mail as suspects. A quick test on staging:

  1. Enable a basic payment method such as bank transfer.
  2. Disable the main payment gateway.
  3. Test checkout again.
  4. Check whether the spinner still appears.

If checkout still hangs with a basic method, the payment provider is probably not the cause. If the trouble really is the payment step, our guide to WooCommerce payment problems covers that side.

Cause 1: AJAX Returning the Wrong Response

WooCommerce answers update_order_review with JSON. When the server returns a normal HTML page instead, the Network tab shows something like:

Status: 200 OK
Content-Type: text/html

A 200 looks successful, but the checkout script cannot use an HTML page, so the overlay stays. If the response body is a full document like this, something intercepted the request:

<!DOCTYPE html>
<html>
<head>
  <title>...</title>
</head>
<body>
...
</body>
</html>

When HTML, a login page or a redirect arrives where JSON belongs, WooCommerce’s guide points to a cache, a maintenance or security layer, a redirect, a proxy or a PHP warning; add .htaccess rewrites to that list. That is why the response body is the first thing to read.

Cause 2: Caching Checkout, Cart, or AJAX Requests

Cart, checkout and My Account must stay dynamic, because they show information specific to each customer. Most caching plugins exclude them already, but migrations, new plugins and host or CDN caches undo that. WooCommerce also lists the cookies to exclude from caching:

woocommerce_items_in_cart
woocommerce_cart_hash
wp_woocommerce_session_

With database caching, exclude _wc_session_ too; for WP Rocket, WooCommerce recommends avoiding JavaScript minification. Every cache, CDN, firewall and proxy layer must pass AJAX and Store API requests through without a stored response. Purge each layer before testing again.

What to check

  • Are cart, checkout and My Account excluded in every cache layer?
  • Do ?wc-ajax= and /wp-json/wc/store/ requests bypass the cache?
  • Does a Cloudflare cache rule make HTML cacheable? Cloudflare does not cache HTML or JSON by default.
  • Is host caching still active with the plugin off? Hosts often install it as a drop-in you cannot deactivate yourself.
  • Is a separate mobile cache serving a different version?
  • Is an optimization plugin minifying or rewriting checkout scripts?

Cause 3: PHP Warnings Breaking AJAX Responses

A checkout response has to be clean. On a normal page a PHP warning shows above the content; printed into an AJAX response, it breaks the JSON. Since PHP 8.0, reading an array key that does not exist raises a warning like this:

Warning: Undefined array key "redirect_cache"

It comes from custom code that reads a key without checking it exists:

$id = $_SESSION['redirect_cache'];

A safer version:

$id = isset($_SESSION['redirect_cache']) ? (int) $_SESSION['redirect_cache'] : 0;

What to check

Look in the response body for warnings, notices, HTML before the JSON, or stray var_dump(), print_r() or echo output. Then log errors instead of displaying them, with WordPress’s standard debug settings in wp-config.php:

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
@ini_set('display_errors', 0);

Errors then go to wp-content/debug.log (or a path you give WP_DEBUG_LOG), including notices raised during AJAX requests. WordPress recommends debug mode for local and staging sites, not live ones, so reproduce the fault on staging. Hiding errors never replaces fixing the code.

Cause 4: JavaScript Errors on the Checkout Page

Checkout leans heavily on JavaScript. An error in another script can stop it updating: a theme or custom checkout script, a missing dependency, a jQuery conflict, a tracking script, or an optimization plugin that changed how scripts load.

What to check

Open the Console in DevTools, clear it and reproduce the spinner. WooCommerce advises saving the first relevant error and its source file, since later errors can be consequences of the first. Look for references to:

checkout
wc_checkout_params
update_order_review
jQuery
payment_method
fragments

Also check whether a content security policy, a consent tool, an ad blocker, an optimization service or a browser extension blocked a checkout script or request. For checkout problems the Network tab usually tells you more than the Console.

Cause 5: Theme or Template Conflicts

Checkout breaks when a theme overrides WooCommerce templates badly: outdated overrides, removed hooks or notices, a layout that hides required elements, CSS that hides payment methods or buttons, or a theme that skips wp_head() or wp_footer(), which WordPress needs to print the scripts checkout depends on.

What to check

Go to WooCommerce → Status. The System Status Report has a Templates section that lists every WooCommerce template your theme overrides and flags outdated ones. Overrides live in:

your-theme/woocommerce/

Compare overridden checkout templates with the current WooCommerce versions. Then, on staging, switch to Storefront or a default theme such as Twenty Twenty-Five. If checkout works, the theme or its overrides are the problem.

Cause 6: Plugin Conflicts

Plugin conflicts are common, but test them methodically rather than by guesswork, and on staging rather than a busy live store.

How to test safely

On staging, update everything first. Then deactivate all plugins except WooCommerce and the extensions involved, and test. If checkout works, reactivate plugins one at a time, testing after each, starting with the likeliest:

  1. Payment plugins
  2. Shipping plugins
  3. Checkout field plugins
  4. Cache and optimization plugins
  5. Security plugins
  6. Custom snippets
  7. Tracking and analytics plugins

If the problem survives with everything off, look at the Must-Use and Drop-ins sections of the plugin list: hosts install some of these, caching drop-ins included, and you cannot switch them off yourself. Tick Disable cache in the DevTools Network panel while you test.

Cause 7: Server-Level Rewrite or Redirect Rules

Sometimes WordPress never sees the request, because a rule in .htaccess, Nginx, LiteSpeed or a CDN answers first. Rules for the site root are the classic trap: in .htaccess, Apache matches a RewriteRule pattern against the path only and passes the query string through, so a homepage rule such as RewriteRule ^$ /home.html [L] also catches:

/?wc-ajax=update_order_review

WooCommerce then gets a static homepage, a redirect, a login page or a firewall message, and checkout spins although WooCommerce itself is fine. This is precisely what happened in one checkout case we traced to an .htaccess rewrite. If rules were added with an AI tool, see AI-generated .htaccess rules that break WordPress.

What to check

Review .htaccess, Nginx and LiteSpeed rules, Cloudflare and redirect plugin rules, server cache rules and firewall logs. Be careful with anything that affects:

/
/checkout/
/cart/
/my-account/
/?wc-ajax=
/wp-json/wc/store/

Also compare the request URL with the addresses under Settings → General: an old domain, a www mismatch or HTTP requests from an HTTPS page can get checkout requests blocked or redirected. Ask your host before changing those addresses.

Cause 8: Security Firewall or ModSecurity Blocking Requests

Checkout requests carry addresses, payment data, nonces and session data, and a security plugin or a web application firewall such as ModSecurity can decide one looks suspicious. WooCommerce’s guide reads a 403 on a checkout request as a firewall, a security rule, an expired or missing nonce, or another authorization check.

What to check

In the Network tab, inspect the failed request for a 403, firewall HTML, a captcha or challenge page, or messages like “Access denied”. Behind Cloudflare, a challenged response carries the header cf-mitigated: challenge and is always HTML, whatever was requested. If you suspect the server firewall, ask your host to check its logs at the exact time you recorded.

Cause 9: Checkout Field Customization Problems

Custom checkout fields break checkout when added carelessly: required fields without validation, invalid keys, unsanitized values, address fields removed the wrong way, or JavaScript expecting a field that no longer exists. On the classic checkout, change fields through the woocommerce_checkout_fields filter, not by deleting markup:

add_filter('woocommerce_checkout_fields', function($fields) {
    unset($fields['billing']['billing_address_2']);

    if (isset($fields['billing']['billing_phone'])) {
        $fields['billing']['billing_phone']['required'] = true;
    }

    return $fields;
});

That filter only applies to the shortcode checkout; the Checkout block has its own API for additional fields. WooCommerce also warns that its Checkout Field Editor extension overrides custom field code, so do not run both. Never edit WooCommerce core files or hide required fields with CSS alone.

Cause 10: Missing WooCommerce Pages or Wrong Page Settings

WooCommerce needs its cart, checkout and account pages. Go to WooCommerce → Settings → Advanced and confirm that Page setup assigns the right Cart page, Checkout page, My account page and, if you use one, Terms and conditions page. Then confirm the checkout page contains the Checkout block or the shortcode:

[woocommerce_checkout]

WooCommerce notes that the shortcode version sometimes works better with extensions. Even so, do not swap one checkout type for the other as a first troubleshooting step: its guide warns that switching can hide the original compatibility problem.

Cause 11: REST API or admin-ajax Issues

The Checkout block depends on the Store API, which runs on the WordPress REST API under /wp-json/. Block the REST API and the block checkout cannot load or submit, and the block editor fails too, since the REST API is its foundation. Plugins that use WordPress’s own AJAX system send requests to /wp-admin/admin-ajax.php, so a block there can break features a gateway or shipping plugin adds to checkout.

/wp-json/wc/store/v1/cart
/wp-admin/admin-ajax.php
/?wc-ajax=...

Possible blockers include a security plugin that disables the REST API, a firewall, a server rule, a malformed redirect, mixed HTTP and HTTPS content, a caching layer or a wrong site URL.

How to Debug WooCommerce Checkout Loading Properly

Step 1: Reproduce the issue safely

Use a staging copy with live gateways disconnected or in test mode; WooCommerce warns that staging can still charge a payment method, send email and call webhooks. Test one simple, in-stock product without a coupon, and note the address, shipping and payment method, time, browser and device.

Step 2: Open the Network panel

In DevTools, select Network, filter on Fetch/XHR and tick Preserve log. Clear the list and repeat the action. Look for wc-ajax=update_order_review or wc-ajax=checkout on the classic checkout, or requests under /wp-json/wc/store/ on the block. If it stalls after Place order, check WooCommerce → Orders before retrying: the order may already exist.

Step 3: Read the status code

Per WooCommerce’s guide: a long-pending request points to slow PHP, database work, an external API, email or a resource limit; 403 to a firewall, security rule or nonce; 404 to a wrong URL, rewrite rule or proxy route; 500 to a PHP fatal error. A mixed-content or cross-origin error means the page and endpoint disagree about HTTPS, hostname, port or origin.

Step 4: Check the response headers

Look at Content-Type, Cache-Control, Location and cf-mitigated. text/html where JSON belongs means read the body next; Location means a redirect; cf-mitigated: challenge means Cloudflare challenged the request.

Step 5: Check the response body

Look for a full HTML page, a PHP warning, a login page, a security block, a cached homepage or broken JSON. A response of just -1 can mean an AJAX nonce check failed; a cached page with an expired nonce is one possible reason.

Step 6: Check the Console

Save the first relevant error and its file; ignore extension noise unless it touches checkout scripts. After Place order, the message Unable to fix malformed JSON means the checkout response contained something other than clean JSON.

Step 7: Check the logs

WooCommerce → Status → Logs includes a fatal-errors log; gateway logs usually have to be enabled in the gateway’s settings first. Then check the PHP error log, debug.log, the server error log and any firewall log. A System Status Report shows versions, memory limits, template overrides and active plugins. Raise the memory limit only if a log shows it was exhausted.

Step 8: Test theme and plugin conflicts

On staging, as above. Confirm the culprit by making the problem disappear and reappear with the same combination.

What a Healthy WooCommerce Checkout Request Should Look Like

On the classic checkout, update_order_review finishes quickly and returns JSON with keys such as result, messages, reload and fragments. On the block, the Store API answers with JSON too. No full HTML page, no PHP warning, no cached copy, no firewall block. The totals and payment methods refresh, and Place order creates exactly one order.

What Not to Do

  • Rebuilding checkout immediately
  • Editing WooCommerce core files
  • Hiding errors without finding the cause
  • Disabling random plugins on a live store during peak traffic
  • Assuming the payment gateway is the cause
  • Switching between the Checkout block and the classic checkout as a first fix
  • Clearing cache repeatedly without checking the Network response
  • Raising the memory limit without a memory error in the logs
  • Adding more snippets to “force” checkout to work
  • Ignoring PHP warnings because “the page still loads”
  • Changing .htaccess without testing dynamic requests

The fastest fix usually comes from identifying the exact failing request.

When to Ask for Help

Bring in help when checkout keeps spinning after basic troubleshooting, update_order_review returns HTML instead of JSON, payment methods disappear, Place order does nothing, checkout works only sometimes, PHP warnings appear at checkout, it broke after someone added AI-generated code, or cache exclusions and conflict tests have not revealed the cause. Checkout issues are usually fixable, but guessing wastes time and can create new problems.

Final Thoughts

A WooCommerce checkout stuck loading is rarely a mystery once you follow the request. Stop watching the spinner and check what the server returns. Often the problem is not WooCommerce but something around it: caching, rewrites, PHP output, security rules, theme overrides, JavaScript or plugins. The safest fix restores WooCommerce’s native flow instead of replacing it.


Checkout still spinning? Our WooCommerce checkout fix service starts from €299 excl. VAT, with a free assessment, a response within 1 hour during business hours, a fixed price agreed in writing before work starts, and a 14-day recurrence warranty. Want to rule out the payment side first? Our free guide to WooCommerce payment problems walks through it. Or send us the issue and we’ll trace it from the spinner back to the request that is breaking.

Sources

Last checked 12 November 2026.

Chat on WhatsApp