"Checkout not working" is six different problems that share one phrase. The customer who sees a blank page, the one whose card is rejected at the gateway, the one whose page loads but the "Place order" button does nothing, and the one whose request returns a 403 are all looking at distinct root causes. The first job is identifying which of those failure modes is actually happening. Once you know that, the diagnostic path becomes short. Skip the identification step, and you spend hours testing fixes that were never going to apply.
What "checkout not working" actually means
The phrase covers at least six distinct failures, each with a different root cause and a different fix:
- Blank page or infinite spinner. The checkout page renders nothing, or the "Place order" button starts spinning and never stops. Usually a JavaScript conflict or a PHP fatal error during checkout submission.
- Cart is empty when the customer reaches checkout. Items added to cart vanish on the way to the checkout page. Almost always a session cookie problem, often caused by a CDN or WAF stripping cookies.
- No payment method appears at checkout. The customer reaches the checkout page, fills in their address, and finds no way to pay. Usually a Block Checkout incompatibility or an SSL configuration problem.
- 403 Forbidden when submitting the order. The customer fills the form, clicks "Place order", and gets a 403 page. Almost always a Web Application Firewall (WAF) flagging the request.
- Payment fails at the gateway. The order is created in WooCommerce but the gateway rejects the payment. The cause lives at the gateway, not in WooCommerce.
- Order is created but never confirmed to the customer. The order exists in WooCommerce but the customer sees an error or no confirmation. Usually a transactional email or thank-you page redirect issue.
If you do not yet know which one applies, work through the next section before doing anything else.
Identify your symptom first
Match what the customer is actually seeing to one of these patterns, then jump to the section listed in the right column. Do not start fixing before you have identified the symptom; the wrong fix wastes hours.
| What the customer sees | Most likely cause | Where to go next |
|---|---|---|
Blank page at /checkout/ or infinite spinner on "Place order" |
JavaScript conflict, server cache serving stale HTML, or PHP fatal error | "First, rule out caching at every layer" then "Read the error log for fatal PHP errors" |
| Cart is empty on the checkout page | Session cookies stripped by CDN, WAF, or consent banner | The WooCommerce cart not working guide covers session cookie diagnosis in detail |
| No payment method appears at checkout | Block Checkout incompatibility, missing SSL, gateway in wrong mode | The WooCommerce payment gateway not showing guide covers all five common causes |
| 403 Forbidden when clicking "Place order" | WAF false positive, often on Order Attribution cookies | "Order Attribution and WAFs" below |
"result":"failure" in the AJAX response or "security check failed" |
Stale nonce on a cached checkout page | "First, rule out caching at every layer" below |
| Card declined at the gateway, "merchant not found", auth error | Gateway credentials, mode mismatch, or live keys on staging | "Payment method does not appear at checkout" and the linked gateway article |
| Order is "Pending payment" forever, no confirmation email | Customer left the gateway flow before returning, or thank-you page redirect broken | Check WooCommerce > Orders for the actual order state before assuming anything failed |
The decision tree above resolves most cases inside the first minute. Two of the rows route you straight to a dedicated sibling article because the cause has its own diagnostic path that does not belong inside this hub. Use the sections below for the failures this hub article covers in full.
First, rule out caching at every layer
Server-level caching is the single most common cause of mysterious checkout failures, and the misconception that "I cleared my browser cache" or "I cleared the WP Rocket cache" is enough is the reason these problems persist for weeks before being correctly diagnosed.
WooCommerce sets the DONOTCACHEPAGE constant on cart, checkout, and my-account pages. Every WordPress-level caching plugin that respects this constant will skip those pages. The problem starts when a cache layer sits outside WordPress and never sees the constant: nginx fastcgi_cache, Varnish, Cloudflare APO, or LiteSpeed server-level cache. WooCommerce's caching configuration guide is explicit about which URLs and cookies must be excluded from any cache layer.
How to check
- Open the checkout page in an incognito window.
- Open the browser's Network tab (F12 > Network) and reload the page.
- Click the main document request and look at Response Headers for
x-cache,cf-cache-status,age,x-proxy-cache, orx-nginx-cache. - If
x-cache: HITorcf-cache-status: HITappears, orageshows a number above 0, the checkout page is being cached. That is the problem.
A common variant: the checkout page loads fine but submitting it returns "result":"failure" or a security check error. That is a stale nonce on a cached checkout page. WordPress nonces use a tick-based expiry system with a 24-hour lifetime split into two 12-hour ticks. When a server cache stores the checkout HTML and serves it 13 hours later, the nonce in that HTML is already invalid.
How to fix it
Exclude the cart, checkout, my-account, and ?wc-ajax= URLs from your server-level cache. Also exclude requests carrying any of these cookies:
| Cookie | Purpose |
|---|---|
woocommerce_items_in_cart |
Tracks whether the cart has items |
woocommerce_cart_hash |
Detects cart content changes |
wp_woocommerce_session_* |
Links the browser to a server-side session |
After applying the exclusion, clear the full cache and test in a new incognito window. You know it worked when the checkout page returns x-cache: MISS or cf-cache-status: BYPASS, and the age header is absent or 0. The WooCommerce cart not working guide walks through nginx and Cloudflare exclusion rules in detail.
Classic checkout vs. block checkout
WooCommerce 8.3 (November 2023) made the Cart, Checkout, and Order Confirmation blocks the default checkout experience for new installations. Existing stores that updated to 8.3 or later kept their classic shortcode checkout. The two systems behave differently and fail differently. Knowing which one your store runs determines what fix applies.
How to tell which one your store uses
- In wp-admin, go to Pages and edit the Checkout page.
- If you see a single block named "Checkout" with shipping, billing, and payment sections rendered as a visual layout, you are running the block checkout.
- If you see a Shortcode block containing
[woocommerce_checkout], you are running the classic checkout.
Failure mode differences
The two systems fail in distinct ways:
- Classic checkout uses jQuery and PHP hooks. It fails when a JavaScript error breaks the click handler, when a PHP fatal error happens during the AJAX submission, or when a plugin's PHP filter modifies the checkout fields incorrectly.
- Block checkout uses a JavaScript API and React. It fails when an extension is not declared compatible with the block (and is silently hidden rather than appearing broken), when an older payment gateway has not implemented the Block Checkout payment method API, or when a JavaScript optimization plugin defers or combines scripts and breaks the React initialization.
If a checkout extension was working a year ago and stopped after a major WooCommerce update, the block migration is the most likely cause. Check the extension's changelog for "block compatibility" or "Cart and Checkout block support" notes.
Temporary workaround: switch to classic
If you suspect the block checkout is the cause, switch to classic temporarily to isolate the issue:
- Edit the Checkout page in wp-admin.
- Delete the Checkout block.
- Add a Shortcode block containing
[woocommerce_checkout]. - Save and test in an incognito window.
If the problem disappears under classic checkout, the block migration is the issue. The classic shortcode remains supported with no announced deprecation timeline, so this is a valid medium-term workaround while you wait for an extension to add block support.
Run a clean-slate conflict test (Health Check Troubleshooting Mode)
If caching is ruled out and the symptom persists, a plugin or theme conflict is the next suspect. The temptation is to deactivate all plugins and switch to a default theme on the live site. Do not do this on a live store. That puts every visitor's checkout into the broken state for the duration of your test. The general WordPress plugin conflict guide covers folder-rename bisection and debug-log reading for cases where wp-admin itself is unreachable; the WooCommerce-specific path below uses the safer Health Check approach because the store is still live.
The Health Check & Troubleshooting plugin lets you run the conflict test in a way that only affects your own admin session. Visitors continue to see the normal site. WooCommerce documents this approach in the official troubleshooting guide.
Critical warning before you start
WooCommerce is explicit on this point: "Troubleshooting Mode does not put a payment gateway into sandbox mode; if an order is placed while the site is in troubleshooting mode, live payment will be taken." That means any test order you complete during this process charges a real card. Use a test card or your own card and refund it, or test only up to (not through) the gateway redirect.
Steps
- Install Health Check & Troubleshooting from Plugins > Add New.
- Go to Tools > Site Health > Troubleshooting.
- Click Enable Troubleshooting Mode. Your admin session now sees a stripped-down site; visitors still see the normal one.
- Under Plugins, only WooCommerce remains active by default. Re-enable the active payment gateway plugin.
- Test the checkout. Add a product, fill in billing details, and proceed up to the payment step (do not complete the payment unless you are using a test card).
- If the checkout works, the cause is one of the deactivated plugins. Re-enable plugins one at a time, retesting after each, until the failure returns.
- If the checkout still fails with only WooCommerce active, switch the active theme to Storefront or Twenty Twenty-Five under the Theme tab in Troubleshooting Mode. Test again. If it now works, the theme is the cause.
- When done, click Disable Troubleshooting Mode. Your normal site returns immediately.
You know the conflict test succeeded when you have identified one specific plugin or the theme as the trigger. If neither, the cause is not a plugin or theme conflict; move to the next section.
Cart contents disappear before reaching checkout
If the customer's cart is full on the shop page but empty on the checkout page, the failure happened before the checkout even loaded. The session cookie that links the browser to the server-side cart was lost.
Common causes: a CDN configured to strip all cookies from responses, a WAF rule that blocks cookies matching a pattern, a cookie consent banner that blocks all cookies until consent is given, or a browser privacy extension stripping session cookies.
This is its own substantial topic and the WooCommerce cart not working guide covers the diagnosis and fix in full, including how to confirm cookies are being set, which CDN and WAF settings affect them, and how to classify them as functional under a consent banner.
Payment method does not appear at checkout
The customer reaches the checkout page but sees no way to pay. The five most common causes are Block Checkout incompatibility (an older gateway has not implemented block support), missing SSL (gateways hide themselves when the connection is not HTTPS), test mode mismatch (live keys with test mode selected, or vice versa), plugin conflicts blocking the gateway's JavaScript, and geographic restrictions silently excluding the customer's country.
This is a distinct enough problem that it has its own article. The WooCommerce payment gateway not showing guide walks through each cause with diagnostics and fixes, including the specific block-compatibility status of major gateways (Stripe, PayPal Payments, Mollie) and the gateway-specific log files to read.
Read the error log for fatal PHP errors during checkout
If the checkout page is blank, the spinner runs forever, or the AJAX response returns HTML instead of JSON, a PHP fatal error during checkout submission is a strong suspect. WooCommerce's endless loading spinner guide flags this explicitly: when the AJAX endpoint returns HTML rather than JSON, an index.html in the WordPress root or a PHP error in the response is the cause.
Where to find the error
The fastest path depends on your hosting setup:
- WordPress debug log (recommended). Enable
WP_DEBUG,WP_DEBUG_LOG, andWP_DEBUG_DISPLAY = falseinwp-config.php. The enabling and reading the WordPress debug log guide walks through this safely; in particular it covers why you must explicitly setWP_DEBUG_DISPLAY = falseto avoid printing errors to visitors. - Hosting panel error log viewer. Most cPanel and Plesk installations expose the PHP error log in the hosting panel. cPanel's "Errors" tool and Plesk's "Logs" tab show the same data the debug log captures.
- WooCommerce log. Go to WooCommerce > Status > Logs. Gateway-specific failures are recorded here separately from PHP errors.
What to look for
Reproduce the failed checkout, then immediately read the most recent log entries. PHP fatal errors look like:
PHP Fatal error: Uncaught Error: Call to undefined function some_function() in
/wp-content/plugins/some-plugin/checkout-handler.php:42
The path in the file reference points to the plugin or theme responsible. From there, the fix is either updating the plugin (a recent WooCommerce update probably broke an old hook) or contacting the developer with the exact error.
If the error references a WooCommerce core file rather than a plugin, the most likely cause is a plugin filter or action that is calling deprecated WooCommerce code. The same diagnostic identifies the responsible plugin: read the stack trace below the fatal error line.
PHP version compatibility on older payment gateway plugins
WooCommerce currently requires PHP 7.4 minimum and recommends PHP 8.1 or higher. Most modern hosts default to PHP 8.1, 8.2, or 8.3. That is the right choice for performance and security.
It is also where older payment gateway plugins quietly break. Plugins written before PHP 8.0 sometimes use:
- Implicit string-to-int conversions that PHP 8.x now warns about or rejects.
- Deprecated function signatures (the
each()function, removed in PHP 8.0). - Required parameters declared after optional ones, which PHP 8.0 deprecates and PHP 8.4 will remove.
- Reliance on PHP 7-style error handling that becomes a fatal in PHP 8.
The symptom: an older custom or niche payment gateway worked under PHP 7.4 and stopped working after the host upgraded the site to PHP 8.x. The checkout submission produces a fatal error in the PHP log. The fix is to update the gateway plugin (modern releases of major gateways like Stripe, PayPal Payments, and Mollie all support PHP 8.x) or, if no update exists, switch hosting back to PHP 7.4 temporarily while planning a migration to a maintained alternative.
To verify the PHP version your store is using, go to WooCommerce > Status and check Server Environment > PHP version. To change it, use your hosting panel's PHP selector (cPanel's "MultiPHP Manager" or Plesk's "PHP Settings"). Test the checkout in an incognito window after each change.
Order Attribution and WAFs (introduced in WooCommerce 8.5)
Order Attribution Tracking was introduced in WooCommerce 8.5 (January 2024) and is enabled by default on new and existing stores. It records where an order originated (organic search, paid ad, referral, direct) using a set of cookies based on the Sourcebuster library, and surfaces the data in the order admin.
The compatibility issue: some Web Application Firewall rulesets flag Order Attribution cookie content as suspicious and respond with a 403 to the checkout submission. Comodo WAF and the OWASP Core Ruleset have both produced false positives on these cookies. The customer fills in the checkout, clicks "Place order", and sees a 403 Forbidden page or a generic "Sorry, this request looks dodgy" block.
How to confirm Order Attribution is the cause
- In wp-admin, go to WooCommerce > Settings > Advanced > Features.
- Find Order Attribution and disable it.
- Test the checkout in an incognito window.
- If the 403 disappears, Order Attribution cookies are tripping a WAF rule.
How to fix it without disabling the feature
WooCommerce 9.0 introduced the wc_order_attribution_use_base64_cookies filter, which encodes the cookie payload in Base64 to avoid the substrings that trigger most WAF rules. Add this snippet to a site-specific plugin or your theme's functions.php (editable via Appearance > Theme File Editor or your hosting panel's file manager):
// Encode Order Attribution cookies in Base64 to avoid WAF false positives
add_filter('wc_order_attribution_use_base64_cookies', '__return_true');
Re-enable Order Attribution in WooCommerce > Settings > Advanced > Features and test again. The cookies are now Base64-encoded and most WAFs stop flagging them.
If the 403 persists, the WAF is matching a different rule. Contact your hosting provider with the request URL, the timestamp of a reproduced failure, and the WAF brand (Cloudflare, Sucuri, Wordfence, hosting-bundled). They can locate the matched rule in the WAF log and either tune it or whitelist the request pattern.
When clearing the browser cache will not help (and what will)
A persistent misconception in WooCommerce support threads: "I cleared my browser cache and the checkout still does not work." Browser cache and server cache are two separate things, and almost every cache-related checkout failure lives at the server side.
The browser cache stores your local copy of static assets (CSS, JS, images). Clearing it forces a fresh download from the origin. That helps when a stale CSS file is hiding a button, but it does nothing when the issue is a server-level cache, a CDN edge, or a stale nonce in cached HTML.
To rule out server caching, do the following instead of clearing the browser:
- Open an incognito window (private browsing). This bypasses both the browser cache and any logged-in session.
- Add
?nocache=1to the checkout URL, or any unique query parameter the cache layer does not match. Many caches treat query-stringed URLs as uncacheable. If the checkout works with the parameter and fails without it, server cache is the cause. - Inspect response headers in DevTools (F12 > Network) as described in "First, rule out caching at every layer" above.
Clearing the WordPress-level caching plugin's cache (WP Rocket, WP Super Cache, LiteSpeed Cache, W3 Total Cache) is necessary but not sufficient. If you also run nginx fastcgi_cache, Varnish, Cloudflare, or LiteSpeed server-level cache, each of those has its own purge mechanism and must be cleared separately. The hosting panel's caching section usually has a "Purge all" button for whichever server-level cache is bundled.
A second misconception worth flagging: WooCommerce's own Test mode option (in payment gateway settings) blocks payments from going through, but does not block the checkout page from loading. So a checkout failure on a store in test mode is not "because the store is in test mode". The page still has to render correctly.
When to escalate
If none of the sections above identifies the cause, or you have identified it but cannot fix it (a hosting-side WAF you do not control, a server cache layer your provider manages), gather this information before contacting your hosting provider or developer:
- The exact symptom from the decision-tree table at the top of this article.
- WordPress version, WooCommerce version, and PHP version (visible in WooCommerce > Status > System status).
- A full WooCommerce > Status > Get system report export. This lists every plugin, theme, gateway configuration, and key setting in one block.
- A screenshot of the browser console (F12 > Console) showing any errors during the failing action.
- A screenshot of the Network tab (F12 > Network) filtered to
wc-ajax, showing the request URL, HTTP status, and response body. - A copy of the relevant
debug.logexcerpt covering the timestamps of a reproduced failure. - Whether the problem started after a specific event (a WordPress update, a WooCommerce update, a plugin update, a hosting migration, an SSL change, a CDN change).
- Whether the problem affects all customers, all browsers, or a specific subset.
- Confirmation of which checkout type the store uses (block or classic) and whether you have already tested the other.
The first response from any provider or developer who does not have this information will be "try disabling plugins", which is the slowest possible diagnostic path. Sending the system report up front saves a day.
How to prevent recurrence
- After every major WooCommerce update (8.x to 9.x, 9.x to 10.x), test the full purchase flow on a staging environment before the update reaches production. Add to cart, go to checkout, complete payment with a test card, verify the order in WooCommerce > Orders.
- Pin payment gateway plugin updates behind a manual review. Two recent WooCommerce Stripe releases (9.4.0 in April 2025 and 9.5.0 in May 2025) caused checkout breakage and required hotfix releases. An auto-updated production gateway is a single point of failure.
- Document your cache configuration so the next person diagnosing a cart issue does not waste time discovering that nginx fastcgi_cache exists in front of WordPress. A short note in a runbook or
wp-content/uploads/README naming each cache layer and how to purge it pays for itself the first time it is needed. - After any WAF or CDN change, test the checkout end-to-end. WAF rule changes are silent until a real customer hits a 403.
- Keep PHP versions current. PHP 8.1 reaches its end of security support in November 2026, so plan a move to 8.2 or 8.3 well in advance and test gateway compatibility on the new version on staging first.