Skip to content

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 seeMost likely causeFix
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".

bash
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.txt

The 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 printsWhat it means
add: 200, then {"fragments":, then "items_count":1The 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":0WooCommerce refused this product. It cannot be bought as it is set up.
add: 200, then {"fragments":, then "items_count":0The product was added and then forgotten. The session is not being kept.
add: 403, add: 500 or another numberThe 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.

  1. Browser
  2. DNS
  3. HTTPS
  4. CDN or firewall
  5. Web server
  6. PHP
  7. WordPress (this error comes from here)
  8. Database and files

What causes it

  • The product cannot be bought as it is set up

    Common

    WooCommerce 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.

    Fix: Give the product a price, stock and a priced variation

  • A page cache or CDN is storing the cart

    Common

    The 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.

    Fix: Keep the cart out of every cache

  • A JavaScript error stops the button

    Common

    On 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

    Sometimes

    The 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

    Sometimes

    A 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.

    Fix: Put WooCommerce's own template back

  • The Cart page is missing, unassigned or has lost its block

    Sometimes

    WooCommerce 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.

    Fix: Restore the Cart page

  • The store cannot remember the visitor between pages

    Rare

    A 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.

    Fix: Let the store remember the visitor

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 showsWhat is missingThe shop shows
The title, with no price and no buttonA 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."

  1. Step 1: Open the product

    Go to Products, find the product and click Edit. Check that it is published.

  2. 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.

  3. 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.

  4. 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.

  5. 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."

bash
wp wc product get 123 --user=your-admin-username --fields=name,type,status,price,in_stock,purchasable

An 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:

bash
wp wc product update 123 --regular_price=12.00 --user=your-admin-username
bash
wp wc product update 123 --in_stock=true --user=your-admin-username

For a variable product, list its variations. The first column is each variation's own ID.

bash
wp wc product_variation list 123 --user=your-admin-username --fields=id,price,in_stock,purchasable

Then give a variation its price, with the product's ID first and the variation's ID second:

bash
wp wc product_variation update 123 456 --regular_price=12.00 --user=your-admin-username

Keep 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.

  1. 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, private with this page. An Age header with a number above zero means the copy you were sent had been sitting in a cache for that many seconds.

    bash
    curl -s -o /dev/null -D - "https://example.com/cart/"
  2. 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.

  3. 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".

  4. 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.

    text
    woocommerce_cart_hash
    woocommerce_items_in_cart
    wp_woocommerce_session_
  5. 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.

  1. 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.

  2. 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".

  3. Step 3: Read the error

    An error is listed with a file name and a line number. A path through wp-content/plugins/ or wp-content/themes/ names the plugin or theme the file belongs to. Copy the whole message before you change anything.

  4. 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.

  5. 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.

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.

  1. 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 is batch, 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.

  2. Step 2: Read the status

    StatusWhat it meansNext
    403, and the response is a firewall's or a security plugin's pageThe 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
    500PHP 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
  3. Step 3: Run the test from a terminal

    Run the commands under "Test the cart without a browser". add: 403 or add: 500 confirms that the fault is on the server's side and not in your browser.

  4. Step 4: For a 500, read WooCommerce's log

    Go to WooCommerce, then Status, then Logs, and open the newest fatal-errors log. Or print its last entry:

    The entry names the file that failed. The folder after wp-content/plugins/ or wp-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.

    bash
    grep -h "CRITICAL" wp-content/uploads/wc-logs/fatal-errors-*.log | tail -n 1 | cut -c1-300
  5. Step 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.

  6. Step 6: List must-use plugins and drop-ins

    Deactivating every plugin on the Plugins screen leaves two kinds running. List them:

    bash
    wp plugin list --status=must-use --fields=name,status
    wp plugin list --status=dropin --fields=name,status
  7. Step 7: Take a must-use file out

    A must-use plugin is a file in wp-content/mu-plugins and 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 of file-name.php:

    bash
    mkdir -p ~/mu-plugins-off
    mv wp-content/mu-plugins/file-name.php ~/mu-plugins-off/
  8. Step 8: Test again

    Run the test commands once more. A store that is working prints add: 200 and "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.

  1. 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:

    FileDraws
    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.php and variation-add-to-cart-button.phpThe options and the button on a variable product
    single-product/add-to-cart/grouped.php and external.phpGrouped and external products
  2. Step 2: Update the theme

    A current version of the theme usually carries current copies. Take a backup first.

  3. 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.

bash
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:

bash
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:

bash
wp wc tool run clear_template_cache --user=your-admin-username

Reload 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.

  1. Step 1: Read what WooCommerce reports

    Go to WooCommerce, then Status. Under "WooCommerce pages", the Cart line reads one of these:

    The Cart line saysWhat 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.
  2. 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.

  3. 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.

  4. 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.

  5. 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.

bash
wp option get woocommerce_cart_page_id

Run the tool that creates the missing pages. It also picks up an existing Cart page that is no longer chosen.

bash
wp wc tool run install_pages --user=your-admin-username

To 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.

bash
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.

  1. 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.

  2. 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.

  3. 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 curl command after these steps prints them.

  4. 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.com is not there at example.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.

  5. 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.

bash
wp db tables '*woocommerce_sessions' --all-tables

Create what is missing:

bash
wp wc tool run verify_db_tables --user=your-admin-username

Then read the cookies the store sets when a product is added:

bash
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:

bash
wp config get COOKIE_DOMAIN

If 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.

bash
wp config delete COOKIE_DOMAIN

Run 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.

More on this subject

Would you rather we fixed it?

Emergency Fix is $99. Site down or checkout broken. Goes to the front of the queue. No fix, no fee. 30-day warranty. It starts with a free diagnosis.