WooCommerce add to cart not working: find the cause and fix it
What you see points to the cause. A missing button means a product that cannot be bought or an old theme template. A button that does nothing or spins means a script error or a blocked request. A cart that empties means a cache, a missing Cart page or a lost session.
- By
- WP Ministry
- Published
- Tested on
- WordPress 7.1.3, WooCommerce 11.2.0, PHP 8.3.35
In short
- Sort the symptom first. A missing button, a button that does nothing, one that spins and a cart that empties each have a different cause.
- Test in a private window without logging in. A store can work for its logged-in owner and fail for every customer.
- A product with no price, no stock or no priced variation gets no button. The shop shows "Read more" in its place.
- Cart, Checkout and My account must never come from a cache, and a visitor who carries WooCommerce's cart cookies must never be given a stored page.
- Four short commands add a product and read the cart back with no browser. The result separates a fault on the server from a fault in the page.
- Turning off "Enable AJAX add to cart buttons on archives" makes the shop's buttons plain links that need no script, which keeps orders coming while you find the fault.
"Add to cart not working" covers several different faults: the button is missing, it does nothing, it spins without finishing, or the product is added and the cart is empty on the next page. Each one points to a different cause. Find what you see in the table below, confirm the cause with its check, and do that one fix.
Products, past orders and customer accounts are not affected. What you lose is each sale that cannot start while it lasts. The failed orders calculator puts a figure on that from your own numbers.
If customers can fill a cart and are stopped at the last step, the fault is further on: see WooCommerce checkout not working. If the store can take no orders at all right now, the checkout down runbook gives the order to work in.
Sort the symptom first
Test in a private browser window, without logging in. Caching plugins commonly serve their stored pages only to visitors who are not logged in, so a store can work for you and fail for everyone else.
| What you see | Most likely cause | Fix |
|---|---|---|
| No button on one product. The shop shows "Read more" for it. | The product cannot be bought. | Give the product a price, stock and a priced variation |
| No button on any product. | The theme holds an old template. | Put WooCommerce's own template back |
| The button is there and nothing happens. It works on the product page and not on shop or category pages. | A JavaScript error. | Find the script error |
| The button spins and never finishes. | The request is refused or fails. | Find what refuses the request |
| The product is added and the cart is empty on the next page. | A cache stores the cart. If no cache, the session is not kept. | Keep the cart out of every cache, then Let the store remember the visitor |
| "View cart" leads to the home page, or to a page with a title and no cart. | The Cart page is missing or unassigned. | Restore the Cart page |
| It works logged in and fails in a private window. | A cache first. Then the session. | The same two fixes as for an empty cart |
Test the cart without a browser
These commands add one product to a cart and read the cart back, from a terminal on macOS or Linux or from the server over SSH. They take the browser, its extensions and its scripts out of the test.
Use your own address in place of https://example.com/, and in place of 123 the ID of a simple product that should be for sale. Under Products, hover over the product's name and the row shows "ID: 123".
curl -s -c /tmp/cart-test.txt -o /tmp/cart-add.txt -w "add: %{http_code}\n" -d "product_id=123&quantity=1" "https://example.com/?wc-ajax=add_to_cart"
head -c 60 /tmp/cart-add.txt; echo
curl -s -b /tmp/cart-test.txt "https://example.com/wp-json/wc/store/v1/cart" | grep -o '"items_count":[0-9]*'
rm /tmp/cart-test.txt /tmp/cart-add.txtThe first line sends the request that the button sends on WooCommerce's standard templates, and keeps the cookies the store answers with. The second prints the start of the answer. The third reads the cart through WooCommerce's Store API with those cookies. The last removes the two files.
| It prints | What it means |
|---|---|
add: 200, then {"fragments":, then "items_count":1 | The store adds products and remembers them. The fault is in the page: a script, or a cache between the visitor and the store. |
add: 200, then {"error":true, then "items_count":0 | WooCommerce refused this product. It cannot be bought as it is set up. |
add: 200, then {"fragments":, then "items_count":0 | The product was added and then forgotten. The session is not being kept. |
add: 403, add: 500 or another number | The request is refused or fails before WooCommerce can answer. |
A firewall or CDN that challenges automated requests can refuse curl and let browsers through. Confirm a 403 in the browser, as described under "Find what refuses the request", before acting on it. On a site with plain permalinks, the third line needs ?rest_route=/wc/store/v1/cart in place of wp-json/wc/store/v1/cart.
Where it goes wrong
A page request passes through each of these in turn. This one comes from a plugin.
- Browser
- DNS
- HTTPS
- CDN or firewall
- Web server
- PHP
- WordPress (this error comes from here)
- Database and files
What causes it
The product cannot be bought as it is set up
CommonWooCommerce draws the button only for a product it can sell. A product with no price, a product that is out of stock, and a variable product with no priced variation in stock all get no button.
A page cache or CDN is storing the cart
CommonThe cart is different for every visitor. A cache that stores the Cart page, or that hands a stored page to a visitor who carries WooCommerce's cart cookies, shows a cart that belongs to nobody, and it is empty.
A JavaScript error stops the button
CommonOn shop and category pages the button is run by a script. An error in another plugin's script or in the theme's, often after an update or after scripts were combined or delayed, stops it before any request is sent.
Fix: Find the script error, or Turn the shop's buttons into plain links while you look
The add-to-cart request is refused or fails
SometimesThe button sends a background request to the store. A security plugin, a firewall rule or a must-use plugin can refuse it with a 403, and a PHP error in a plugin's cart code ends it with a 500. The customer sees a button that never finishes.
Fix: Turn the shop's buttons into plain links while you look, or Find what refuses the request
The theme holds an old copy of a WooCommerce template
SometimesA theme can replace the files that draw the button with copies of its own. WooCommerce's files change with its updates and the theme's copies do not, so an old copy can leave the button out or draw one that no longer works.
The Cart page is missing, unassigned or has lost its block
SometimesWooCommerce needs one published page, chosen as the Cart page, that holds the Cart block or the cart shortcode. Without it the product is added and the customer has nowhere to see it.
The store cannot remember the visitor between pages
RareA visitor's cart is kept in a database table and found again through a cookie. If the table is missing, or the cookie is set for a domain the visitor is not on, every page starts a new, empty cart.
How to fix it
Give the product a price, stock and a priced variation
- Easy
- No risk
- About 5 minutes
- Steps tested on WordPress 7.1.3
WooCommerce draws the button only for a published product that has a price and is in stock. What the product page shows instead tells you what is missing.
| The product page shows | What is missing | The shop shows |
|---|---|---|
| The title, with no price and no button | A regular price | "Read more" |
| "Out of stock" | Stock | "Read more" |
| "This product is currently out of stock and unavailable." | A variation that has a price and is in stock | "Read more" |
A visitor who follows an old add-to-cart link to such a product is told "Sorry, this product cannot be purchased." or, with the product's own name in it, "You cannot add "Blue Mug" to the cart because the product is out of stock."
Step 1: Open the product
Go to Products, find the product and click Edit. Check that it is published.
Step 2: Enter a price
In the Product data box, under General, fill in "Regular price". A product with an empty price cannot be bought. A price of 0 can.
Step 3: Set the stock
Under Inventory, set "Stock status" to "In stock". If "Track stock quantity for this product" is ticked, enter a quantity above zero instead.
Step 4: Price every variation of a variable product
Under Variations, open each variation and give it a regular price. A variation with no price is not offered in the store, and a variable product with no variation to offer shows the "unavailable" message. Save the product.
Step 5: Reload the product page
The price and the button are there. On the shop page, "Read more" has become "Add to cart", or "Select options" for a variable product.
With WP-CLI, read what WooCommerce holds for the product. Every wp wc command needs --user with the username or ID of an administrator, or it answers "Sorry, you cannot view this resource."
wp wc product get 123 --user=your-admin-username --fields=name,type,status,price,in_stock,purchasableAn empty price with purchasable false is a product with no price. in_stock false is a product that is out of stock. Set a price, or put it back in stock:
wp wc product update 123 --regular_price=12.00 --user=your-admin-usernamewp wc product update 123 --in_stock=true --user=your-admin-usernameFor a variable product, list its variations. The first column is each variation's own ID.
wp wc product_variation list 123 --user=your-admin-username --fields=id,price,in_stock,purchasableThen give a variation its price, with the product's ID first and the variation's ID second:
wp wc product_variation update 123 456 --regular_price=12.00 --user=your-admin-usernameKeep the cart out of every cache
- Takes care
- Low risk
- About 20 minutes
WooCommerce's caching guide names three pages that must stay out of the cache: Cart, My Account and Checkout. It also lists the cookies that mark a visitor with a cart. A cache that ignores either one shows that visitor a stored page, with a cart that is not theirs.
Step 1: Confirm that a cache is involved
Read the headers the store sends with the Cart page:
WooCommerce itself sends
Cache-Control: no-cache, must-revalidate, max-age=0, privatewith this page. AnAgeheader with a number above zero means the copy you were sent had been sitting in a cache for that many seconds.bashcurl -s -o /dev/null -D - "https://example.com/cart/"Step 2: List every cache in front of the store
There may be more than one: a caching plugin, a cache your host runs, and a CDN.
Step 3: Exclude the three pages from each
Cart, Checkout and My account. If you have changed a page's address, exclude the new address. Many caching plugins leave these pages out on their own, because WooCommerce marks them as not to be cached. It marks the pages chosen under WooCommerce, then Settings, then Advanced, so a page that is no longer chosen there loses that protection. Check the setting as described under "Restore the Cart page".
Step 4: Exclude WooCommerce's cookies
If the cache has a setting for cookies, list these three, so that a visitor with a cart is always given a fresh page:
If the cache also stores database queries, WooCommerce's guide suggests excluding
_wc_session_from it.textwoocommerce_cart_hash woocommerce_items_in_cart wp_woocommerce_session_Step 5: Empty every cache and test again
Use a new private window. Add a product, open the cart, and reload it once. If you cannot see the settings of your host's cache, ask the host to confirm that the three pages and the three cookies are excluded.
Block-based carts and product buttons talk to the store through addresses under /wp-json/wc/store/. WooCommerce marks those answers Cache-Control: no-store. A cache that stores them anyway hands a visitor someone else's cart, along with a security token that has gone out of date. How to speed up a WooCommerce store covers what a store's cache may keep and what it may not.
Find the script error
- Takes care
- Low risk
- About 20 minutes
On shop and category pages the button is run by JavaScript. On WooCommerce's standard page for a simple product it is an ordinary form that needs none. A button that works on the product page and fails on the shop page is the sign of a script error.
Step 1: Rule out your own browser
Try a private window and a second browser. Browser extensions such as ad blockers can stop a site's scripts. If the button works there, the fault is in your browser and not in the store.
Step 2: Open the Console
Open the browser's developer tools: F12 in most browsers, or right-click the page and choose Inspect. In Safari, turn on the Develop menu under Settings, then Advanced, first. Choose the Console tab, reload the shop page and press "Add to cart".
Step 3: Read the error
An error is listed with a file name and a line number. A path through
wp-content/plugins/orwp-content/themes/names the plugin or theme the file belongs to. Copy the whole message before you change anything.Step 4: Switch off script optimization first
If a plugin combines, minifies or delays JavaScript, switch that feature off, empty its cache and test again. WooCommerce's caching guide makes the same recommendation about minifying JavaScript files.
Step 5: Update, then narrow it down
Update the plugin or theme the error names. If the error stays, or names nothing you recognize, test with a default theme and with plugins switched off in halves. How to find and fix a WordPress plugin conflict has the method. Do it on a staging copy of the store if you have one.
While you look, the next fix makes the shop's buttons work without any script.
Turn the shop's buttons into plain links while you look
- Easy
- Low risk
- About 5 minutes
- Steps tested on WordPress 7.1.3
This is a stopgap, not a repair. It removes the script and the background request from the shop's buttons, so customers can buy while the real fault is found.
Step 1: Open the setting
Go to WooCommerce, then Settings, then Products. On the General tab, find "Add to cart behaviour".
Step 2: Untick "Enable AJAX add to cart buttons on archives"
Click "Save changes". Each button on shop and category pages is now an ordinary link. Pressing it reloads the page with the product in the cart. Ticking "Redirect to the cart page after successful addition" has the same effect on the buttons, and also sends the customer to the cart each time.
Step 3: Empty the page cache
A stored copy of the shop page still holds the old buttons.
Step 4: Test in a private window
Press "Add to cart" on the shop page. The page reloads, and the cart holds the product.
With WP-CLI:
wp option update woocommerce_enable_ajax_add_to_cart noThis checks that the store adds a product from such a link, with no script and no background request. It prints "items_count":1.
curl -s -c /tmp/cart-test.txt -o /dev/null "https://example.com/?add-to-cart=123"
curl -s -b /tmp/cart-test.txt "https://example.com/wp-json/wc/store/v1/cart" | grep -o '"items_count":[0-9]*'
rm /tmp/cart-test.txtThe commands show the server's side. How a browser draws the button depends on the theme, and a theme that adds its own script to product pages is not changed by this setting. When the fault is fixed, turn the setting back on:
wp option update woocommerce_enable_ajax_add_to_cart yesTo undo it: Tick the box again and save.
Find what refuses the request
- Takes care
- Low risk
- About 30 minutes
- Steps tested on WordPress 7.1.3
A button that never finishes has sent its request and received no answer it can use.
Step 1: Watch the request in the browser
Open the developer tools, choose the Network tab and press "Add to cart". A new request appears. On WooCommerce's standard templates its name begins
?wc-ajax=add_to_cart. On block-based product grids it isbatch, sent to/wp-json/wc/store/v1/batch. The Status column holds the answer's number. Click the request and open the Response tab to read what came back.Step 2: Read the status
Status What it means Next 403, and the response is a firewall's or a security plugin's page The request was refused before it reached WooCommerce. The steps below, and 403 Forbidden 403, and the response reads woocommerce_rest_invalid_nonceWooCommerce itself refused the security token sent with the request. The token arrives with the Store API's earlier answers, so suspect a cache that stores them. Keep the cart out of every cache 500 PHP failed while the product was being added. Read the log, below 200, and the response begins {"error":trueWooCommerce refused the product. Give the product a price, stock and a priced variation Step 3: Run the test from a terminal
Run the commands under "Test the cart without a browser".
add: 403oradd: 500confirms that the fault is on the server's side and not in your browser.Step 4: For a 500, read WooCommerce's log
Go to WooCommerce, then Status, then Logs, and open the newest
fatal-errorslog. Or print its last entry:The entry names the file that failed. The folder after
wp-content/plugins/orwp-content/themes/is the plugin or theme at fault: update it or switch it off, and report the error to its author. 500 Internal Server Error covers the case where there is no such log.bashgrep -h "CRITICAL" wp-content/uploads/wc-logs/fatal-errors-*.log | tail -n 1 | cut -c1-300Step 5: For a 403, find the rule
Look in your security plugin's log of blocked requests for the time of your test. Allow requests to
/?wc-ajax=and to/wp-json/wc/store/rather than switching the plugin off for good. If the refusal comes from the host's firewall or a CDN, send them the address, the time and the status.Step 6: List must-use plugins and drop-ins
Deactivating every plugin on the Plugins screen leaves two kinds running. List them:
bashwp plugin list --status=must-use --fields=name,status wp plugin list --status=dropin --fields=name,statusStep 7: Take a must-use file out
A must-use plugin is a file in
wp-content/mu-pluginsand can only be switched off by taking the file out. Hosts put their own files there: ask your host before you move one of theirs. For a file that is yours, move it out of the site's folders, with its name in place offile-name.php:bashmkdir -p ~/mu-plugins-off mv wp-content/mu-plugins/file-name.php ~/mu-plugins-off/Step 8: Test again
Run the test commands once more. A store that is working prints
add: 200and"items_count":1.
WooCommerce sends these requests to an address of its own and not to admin-ajax.php. What is calling admin-ajax.php shows how to count them in the server's access log.
To undo it: Move the file back into wp-content/mu-plugins, or activate the plugin again.
Put WooCommerce's own template back
- Takes care
- Back up first
- About 20 minutes
- Steps tested on WordPress 7.1.3
A theme can carry its own copies of the files that draw the button, in a folder named woocommerce. When the button is missing from every product, or has been since a WooCommerce update, look there.
Step 1: Read the Templates section
Go to WooCommerce, then Status. Near the end of the System status tab, "Templates" lists every file the theme replaces, by its path. Under an old one is a warning such as "Version 3.4.0 is out of date. The core version is 10.2.0".
These are the files that draw the button:
File Draws loop/add-to-cart.phpThe button on shop and category pages single-product/add-to-cart/simple.phpThe form on a simple product single-product/add-to-cart/variable.phpandvariation-add-to-cart-button.phpThe options and the button on a variable product single-product/add-to-cart/grouped.phpandexternal.phpGrouped and external products Step 2: Update the theme
A current version of the theme usually carries current copies. Take a backup first.
Step 3: If the copy is still old, move it aside
Without the copy, WooCommerce uses its own file. Any change of design the copy carried goes with it until the copy is brought up to date.
With SSH, list the copies in every installed theme. The active theme's are the ones that count, and its parent's if it is a child theme.
find wp-content/themes -path "*/woocommerce/*" -name "*.php"Move one copy out of the site's folders, with your theme's folder in place of your-theme:
mkdir -p ~/template-copies
mv wp-content/themes/your-theme/woocommerce/single-product/add-to-cart/simple.php ~/template-copies/WooCommerce remembers where it found each template. Clear that memory, which is the "Clear template cache" tool under WooCommerce, then Status, then Tools:
wp wc tool run clear_template_cache --user=your-admin-usernameReload a product page. The button is back, drawn by WooCommerce's own file.
Bringing a theme's copy up to date is covered in how to update WooCommerce safely. If each update leaves the store to be checked by hand, WooCommerce maintenance is WP Ministry's Store care plan, where updates are tested on a staging copy first and checkout and payment are tested after every update.
To undo it: Move the file back into the theme's woocommerce folder.
Restore the Cart page
- Easy
- Low risk
- About 10 minutes
- Steps tested on WordPress 7.1.3
WooCommerce sends customers to the page chosen as its Cart page. With none chosen, "View cart" leads to the home page. With "Redirect to the cart page after successful addition" ticked, so does the button itself.
Step 1: Read what WooCommerce reports
Go to WooCommerce, then Status. Under "WooCommerce pages", the Cart line reads one of these:
The Cart line says What is wrong #6 - /cart/ - Contains the woocommerce/cart blockNothing. The shortcode [woocommerce_cart]in place of the block is also fine.Page not setNo page is chosen. Page does not contain the [woocommerce_cart] shortcode or the woocommerce/cart block.The page is chosen and has lost its cart. Page visibility should be publicThe page is a draft, private or in the Trash. Step 2: If no page is chosen
Go to WooCommerce, then Settings, then Advanced. Under "Page setup", choose your Cart page in "Cart page" and click "Save changes". Cart, Checkout and My account must be three different pages.
Step 3: If the page is not public
Open it under Pages and publish it. If it is in the Trash, restore it from there.
Step 4: If the page is gone
Go to WooCommerce, then Status, then Tools. Find "Create default WooCommerce pages" and click "Create pages". The tool makes the default pages that are missing, or in the Trash, and leaves the others alone.
Step 5: If the page has lost its cart
Open the page in the editor. Restore an earlier revision, or add the Cart block and update the page. The tool above does not repair a page that is still chosen, whether it is empty, a draft or private.
With WP-CLI, read the setting. It prints the page's ID, or an empty line when no page is chosen.
wp option get woocommerce_cart_page_idRun the tool that creates the missing pages. It also picks up an existing Cart page that is no longer chosen.
wp wc tool run install_pages --user=your-admin-usernameTo replace a Cart page that has lost its cart, move it to the Trash and run the tool again. The page stays in the Trash, where it can be restored. A design of your own on it is not carried over to the new page.
wp post delete "$(wp option get woocommerce_cart_page_id)"Afterward the Cart line on the status screen reads Contains the woocommerce/cart block, and "View cart" opens a cart.
To undo it: Restore the old page from Pages, then Trash, and choose it again under WooCommerce, then Settings, then Advanced.
Let the store remember the visitor
- Takes care
- Back up first
- About 20 minutes
- Steps tested on WordPress 7.1.3
WooCommerce keeps each visitor's cart in a database table and finds it again through a cookie. The test commands show the sign of a lost session: {"fragments": followed by "items_count":0. Rule out a cache first, since a stored page looks the same to a customer.
A logged-in customer's cart is kept under their account, so it can survive when a guest's does not.
Step 1: Check the sessions table
Go to WooCommerce, then Status. Under "Database", a missing table is reported as "Missing base tables: wp_woocommerce_sessions. Some WooCommerce functionality may not work as expected." Your table's name begins with your own database prefix.
Step 2: Create it again
Go to WooCommerce, then Status, then Tools. Find "Verify base database tables" and click "Verify database". WooCommerce creates the tables that are missing.
Step 3: Check the cookies
The store must set three when a product is added, and none may name a domain other than the one your visitors use. The
curlcommand after these steps prints them.Step 4: Check that the store answers at one address
A cookie set without a domain goes back only to the host that set it. A cart filled at
www.example.comis not there atexample.com. Under Settings, then General, "WordPress Address (URL)" and "Site Address (URL)" should hold the address visitors use, and every other form of it should redirect there.Step 5: If only some visitors are affected
Their browser may refuse cookies. WooCommerce's sessions depend on cookies, so there is no cart without them. Check that nothing on the site, such as a cookie-consent tool, holds back the three cookies WooCommerce sets.
With WP-CLI, look for the table. It prints the table's name, or an error if there is none.
wp db tables '*woocommerce_sessions' --all-tablesCreate what is missing:
wp wc tool run verify_db_tables --user=your-admin-usernameThen read the cookies the store sets when a product is added:
curl -s -o /dev/null -D - -d "product_id=123&quantity=1" "https://example.com/?wc-ajax=add_to_cart" | grep -i "^set-cookie"It prints three lines, for woocommerce_items_in_cart, woocommerce_cart_hash and wp_woocommerce_session_. If each carries domain= with a domain that is not your store's, as a move from another address can leave behind, a browser throws the cookies away. The domain comes from a line in wp-config.php:
wp config get COOKIE_DOMAINIf that answers that the constant is not defined, the domain is not your cause. If it prints a wrong domain, back up wp-config.php, then correct the line or remove it. WordPress needs the line only for unusual domain setups.
wp config delete COOKIE_DOMAINRun the test commands again. They print "items_count":1.
To undo it: Put the COOKIE_DOMAIN line back in wp-config.php.
When to get help
If the test commands add a product and read it back, a private window shows no error in the Console, and customers still cannot fill a cart, the cause is in a cache or firewall you cannot see into or in the theme's own scripts. Get help sooner if the store is losing orders by the hour, or if the fault began with an update you cannot undo.
Common questions
Why does add to cart work for me and not for my customers?
You are probably logged in. Caching plugins commonly serve stored pages only to visitors who are not logged in, and a logged-in customer's cart is kept under their account. Test in a private window as a customer would. If it fails there, look at caching first and at the session second.
Why does the button say "Read more" instead of "Add to cart"?
WooCommerce shows "Read more" on shop pages for a product it cannot sell: one with no price, one that is out of stock, or a variable product with no priced variation in stock. Open the product and fill in what is missing.
The button works on the product page and not on the shop page. Why?
On WooCommerce's standard templates the product page's button is an ordinary form, and the shop page's button is run by a script. A fault that touches only the shop page is a script error or a blocked background request. Turning off "Enable AJAX add to cart buttons on archives" makes the shop's buttons plain links in the meantime.
Is it safe to turn off AJAX add to cart?
Yes. Nothing is lost and it can be turned back on at any time. The page reloads each time a product is added, which is slower for the customer and needs no script to work.
Will "Clear customer sessions" fix an empty cart?
No. That tool deletes every visitor's current cart and every saved cart. It does not put back a missing table, a wrong cookie domain or a cache setting, so the next cart is lost in the same way.
Are any orders or products lost?
No. A cart is temporary data kept for one visitor. Products, orders and customer accounts are stored separately, and none of the fixes on this page changes them, apart from the price and stock you set yourself.
- GuideHow to migrate from Shopify to WooCommerce
- GuideHow to set up WooCommerce email notifications
- GuideHow to speed up a WooCommerce store
- GuideHow to update WooCommerce safely: before, on staging, on the live store, and if it breaks
- GuideMismatched value (page crawl) for price in Merchant Center: finding the cause on a WooCommerce store
- GuideMissing field "brand" and "No global identifier provided" in WooCommerce: what they mean, how to fix them

