WooCommerce out of stock and discontinued products and SEO: leave, hide, delete or redirect
An out-of-stock WooCommerce product keeps its page. The address still answers 200 and its structured data changes to OutOfStock, so a product that is coming back needs nothing done. One that is gone for good is redirected to a true replacement or left to answer 404, never sent to the home page.
- By
- WP Ministry
- Published
- Tested on
- WordPress 7.1.3, WooCommerce 11.2.0, PHP 8.3.35
In short
- When a product runs out, WooCommerce changes availability in its structured data to https://schema.org/OutOfStock and removes the add-to-cart button. The address still answers 200, with no noindex.
- "Hide out of stock items from the catalog" takes the product out of the shop, category pages and product search. Its own address still answers 200 and it stays in the sitemap.
- When stock returns, the value goes back to InStock by itself. If the page was left alone, there is nothing to undo.
- A product that is trashed, deleted, draft or private answers 404 to a visitor. WordPress can instead redirect the address to a product with a similar slug, so check what it answers.
- Google's documentation says a removed page with no replacement should answer 404 or 410, and a page with a clear replacement should be redirected to it with a 301.
- On a variable product with one variation sold out, the product's markup still says InStock. Only the address that names that variation says OutOfStock.
When a WooCommerce product runs out, its page stays where it is. The address still answers 200. The page prints "Out of stock" where the add-to-cart button was, and the availability in its structured data changes from https://schema.org/InStock to https://schema.org/OutOfStock. WooCommerce adds no noindex and takes nothing out of the sitemap. When stock returns, the value changes back by itself.
So a product that is coming back needs nothing done to its page. A product that is gone for good is a different decision: redirect its address to a true replacement, or let it answer 404. This page covers both, in two halves. Each half says what WooCommerce does by itself and what Google's documentation says about the answer a crawler then gets. It makes no claim about where a page is listed in search results.
The examples use a store at https://example.com that sells a Blue Kettle at /product/blue-kettle/ and a Red Kettle at /product/red-kettle/. Put your own addresses in their place.
Part one: a product that is out of stock
What changes on the product's page
The stock status is on the product's edit screen, in the "Product data" box under the Inventory tab. "Stock status" has three choices. This is what each one puts on the product's page.
| "Stock status" | availability in the structured data | What a visitor sees | The address answers |
|---|---|---|---|
| In stock | https://schema.org/InStock | The add-to-cart button | 200 |
| Out of stock | https://schema.org/OutOfStock | "Out of stock", and no add-to-cart button | 200 |
| On backorder | https://schema.org/BackOrder | "Available on backorder", and the add-to-cart button | 200 |
If "Track stock quantity for this product" is ticked, there is no "Stock status" to choose. WooCommerce sets the status from the quantity and from "Allow backorders?".
Three things do not change when a product runs out.
- The price stays. The page still shows it, and the offer in the structured data still carries it.
- The robots tag stays. It reads
max-image-preview:largebefore and after. WooCommerce adds nonoindexto a product that is out of stock. - The sitemap stays. The product is still listed in WordPress's product sitemap.
WooCommerce schema markup and rich snippets covers the rest of the structured data on a product page and how to test it.
Read the value your store serves
This asks for a product's page and prints the availability in it.
curl -s https://example.com/product/blue-kettle/ | grep -o '"availability":"[^"]*"'"availability":"https://schema.org/OutOfStock"Without a terminal, open the product's page, view its source, and search for availability.
Change the stock status with WP-CLI
Editing the product and choosing "Out of stock" is the usual way. With many products, WP-CLI does the same from the command line. Put the product's ID in place of 123 and the username of an administrator in place of admin. WooCommerce's commands refuse to run without --user. How to use WP-CLI covers connecting and where to run commands.
wp wc product update 123 --user=admin --in_stock=falseIt answers Success: Updated product 123.
What "Hide out of stock items from the catalog" does
Go to WooCommerce, then Settings, open the Products tab and choose Inventory. The row "Out of stock visibility" holds one checkbox, "Hide out of stock items from the catalog". It is unticked on a new store. WooCommerce stores it as the option woocommerce_hide_out_of_stock_items, which is no while the box is unticked and yes while it is ticked.
wp option get woocommerce_hide_out_of_stock_itemsThis is where a product that is out of stock appears, with the box each way.
| Place | Box unticked | Box ticked |
|---|---|---|
| The shop page | Listed, with "Read more" in place of the add-to-cart button | Not listed |
| Its category page | Listed | Not listed |
A product search (?s=kettle&post_type=product) | Listed | Not listed |
The site's ordinary search (?s=kettle) | Listed | Still listed |
| The product's own address | 200, with OutOfStock | 200, with OutOfStock |
| The product sitemap | Listed | Still listed |
"Related products" on another product's page depends on the theme. With a block theme and WooCommerce's own product template, that section lists only products that are in stock or on backorder, whichever way the box is set. On a classic theme, WooCommerce picks related products with a function that leaves out products that are out of stock only while the box is ticked.
So the box changes your own listings and nothing else. It does not take the page down, does not add noindex, and does not remove the address from the sitemap. A search engine that already knows the address can still fetch the page and read OutOfStock on it. What does change is that the shop and category pages stop linking to the product for as long as the box is ticked and the product is out of stock.
What Google's documentation says availability is for
Google's documentation for merchant listings says a listing can show a product's price, its availability, and shipping and return information. In that documentation availability is a recommended property of the offer, not a required one. It accepts ten values: BackOrder, Discontinued, InStock, InStoreOnly, LimitedAvailability, OnlineOnly, OutOfStock, PreOrder, PreSale and SoldOut, each with or without the https://schema.org/ in front, and it asks for only one value per offer.
WooCommerce prints three of the ten: InStock, OutOfStock and BackOrder. It has no status that produces Discontinued, SoldOut or PreOrder.
The same documentation recommends putting product markup in the HTML the server sends, because availability and price change fast. WooCommerce does that: the value in the table above is in the page as served.
What Merchant Center does with an item that is out of stock
This matters only if your store sends its products to Google Merchant Center. Merchant Center's help page for the availability attribute says these things.
- Availability is required for every product, and takes one of four values:
in_stock,out_of_stock,preorderandbackorder. - It maps the values in your markup to its own.
InStockcounts asin_stock,OutOfStockasout_of_stock, andBackOrderasbackorder.DiscontinuedandSoldOutalso count asout_of_stock. - The values have to agree. The product data, the product's page, its structured data and the checkout must show the same availability. A mismatch gets the product disapproved.
- A product on backorder needs a date. Merchant Center requires an availability date for
backorder, and wants that date visible on the product's page. WooCommerce prints "Available on backorder" with no date, so the date is yours to add to the page. - A product that is temporarily out of stock stays in the product data, marked
out_of_stock. The page advises against deleting an item you will sell again, because an item added back takes a significant time to show again. - A product you no longer sell is removed from the product data, not left there as
out_of_stock.
The product data specification adds that the price must still be visible on the page of an item that is out of stock. WooCommerce keeps it there, as above. Mismatched price in Merchant Center on a WooCommerce store covers the price side of the same check.
When it comes back in stock
Set "Stock status" back to "In stock", or run the command below. The add-to-cart button returns and the structured data says InStock again from the next request. If "Hide out of stock items from the catalog" is ticked, the product returns to the shop and its category by itself.
wp wc product update 123 --user=admin --in_stock=trueIf the page was left published, there is nothing else to undo: the address never changed and never stopped answering 200. If a caching plugin stores whole pages, clear it, so that visitors and crawlers are not served the copy that says "Out of stock".
Part two: a product that is gone for good
What each choice gives a visitor and a crawler
| What you do | The product's address answers | In the product sitemap |
|---|---|---|
| Leave it published and out of stock | 200, with OutOfStock | Yes |
| Set "Catalog visibility" to "Hidden" | 200. The shop, category pages and both searches stop listing it | Yes |
| Switch it to draft | 404 | No |
| Make it private | 404 for anyone who is not logged in | No |
| Move it to the trash | 404 | No |
| Delete it permanently | 404 | No |
This command prints the status an address answers with, and where it redirects to if it does. It does not follow the redirect.
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" https://example.com/product/blue-kettle/404And this one lists the addresses in WordPress's product sitemap. An SEO plugin that makes its own sitemap replaces this one, and the WordPress sitemap at wp-sitemap.xml shows how to tell which one a site serves.
curl -s https://example.com/wp-sitemap-posts-product-1.xml | grep -o "<loc>[^<]*</loc>"What the trash does to the address
Moving a product to the trash does not reserve its address. WordPress renames the slug of a trashed post, so blue-kettle becomes blue-kettle__trashed, and it keeps the original in a field named _wp_desired_post_slug so that a restored post can try to take it back. WordPress has done this since version 4.5.
- Restored before anything took the slug, the product comes back published at its old address.
- If a new product was given the same name in the meantime, the new product has the address. The restored one becomes
/product/blue-kettle-2/. - The trash empties itself. WordPress's documentation gives 30 days as the default, set by
EMPTY_TRASH_DAYSinwp-config.php. After that the product is deleted permanently.
WordPress may redirect the address by itself
A removed product's address does not always answer 404. When WordPress has nothing at an address, it looks for a published post whose slug begins with the same words and, if it finds one, redirects there with a 301.
With a Blue Kettle in the trash and a Blue Kettle Large still published, the command above prints this:
301 https://example.com/product/blue-kettle-large/The answer carries the header X-Redirect-By: WordPress, which is how to tell this redirect from one of your own. It happens the same way for a product that was deleted, or switched to draft.
So check what the old address answers before you decide it is a 404. If WordPress sends it to a product that is not a replacement, a rule of your own for that address answers first. Both rules below do that. WordPress also has a filter, do_redirect_guess_404_permalink, that switches this guessing off for the whole site.
What Google says about each answer
A 404 or a 410. Google's documentation on status codes says it treats every 4xx answer except 429 the same way. It does not use the content, and an address that was indexed is removed from the index. Its guidance on soft 404 errors says that a page you removed, with no replacement that has similar content, should answer 404 or 410. Search Console's help for the Page indexing report adds that a 404 is not necessarily a problem when a page was removed without a replacement.
A redirect. The same guidance says that when a page has moved or has a clear replacement, the old address should answer with a 301 to it. Google's page on redirects says a permanent redirect is a signal that the target should be the address shown in results.
A redirect to something unrelated. Google's guide to moving a site warns against sending many old addresses to one irrelevant destination, such as the home page, and says:
This can confuse users and might be treated as a soft 404 error.
That sentence is written about site moves. Google's pages on status codes, on soft 404 errors and on redirects do not repeat it. It is still the reason not to point every discontinued product at the home page.
A page that stays up. A discontinued product left published answers 200 and says OutOfStock, which is one of the values Google's product documentation accepts. Google describes a soft 404 as a page that tells the visitor it does not exist while answering 200, and says it leaves such pages out of Search. Its documentation does not say whether a product page that has been out of stock for a long time is read that way.
Redirect a discontinued product to its replacement
Use this when a newer model has taken the product's place. The rule goes in .htaccess on an Apache server and answers before WordPress starts, so it works whether the old product is in the trash, deleted or still published. How to set up redirects in WordPress has the other ways: a redirect plugin's list, nginx, and code.
Step 1: Ask what the old address answers now
Run the command under "What each choice gives a visitor and a crawler" for the old product's address. Note whether it prints 200, 404, or a 301 that WordPress made.
Step 2: Add the rule above WordPress's block
Put the rule at the top of
.htaccess, above the line# BEGIN WordPress. WordPress rewrites the lines between its two markers, so a rule placed there is lost..htaccessRedirectMatch 301 "^/product/blue-kettle/?$" "/product/red-kettle/"Step 3: Ask again
The command should now print 301 and the replacement's address.
text301 https://example.com/product/red-kettle/Step 4: Follow it to the end
Open the old address in a browser. It should land on the replacement's page, and that page should answer 200 with no further redirect. The redirect checker shows each hop without a terminal.
Step 5: Move the old product to the trash
With the redirect in place, the old product's own page can no longer be reached. Trash it, so that it leaves the shop and the sitemap.
To undo the redirect, remove the line and save.
Or say plainly that it is gone
For a product with nothing like it, a 404 is enough, and trashing the product gives you one. Use this rule when WordPress redirects the old address to a product that is not a replacement, or when you want the answer to say the page was removed on purpose. It goes in the same place, above # BEGIN WordPress.
RedirectMatch gone "^/product/blue-kettle/?$"The address then answers 410 with Apache's own short page, not your theme's "Page not found" page. Google's documentation treats a 410 the same as a 404.
Which to choose
| The situation | What to do | What the address answers |
|---|---|---|
| Back soon | Leave the page published. Set "Stock status" to "Out of stock", or to "On backorder" if you take orders now | 200, with OutOfStock or BackOrder |
| Back at a date you do not know | Leave the page published and out of stock. Tick "Hide out of stock items from the catalog" if you do not want it in the shop meanwhile | 200, with OutOfStock |
| Replaced by a newer model | Redirect the old address to the new model's page, then trash the old product | 301 to the replacement, which answers 200 |
| Gone, with nothing like it | Trash the product. Check that the address answers 404, and add the 410 rule if WordPress redirects it somewhere unrelated | 404 or 410 |
A variable product with one variation out of stock
On a variable product, each variation has its own stock status. With a Tea Tin sold in Small at 10.00 and Large at 14.00, and Small set to "Out of stock", this is what the pages say.
| Address | The offer in the structured data | availability |
|---|---|---|
/product/tea-tin/ | An AggregateOffer from 10.00 to 14.00, counting 2 offers | https://schema.org/InStock |
/product/tea-tin/?attribute_size=Small | One Offer at 10.00 | https://schema.org/OutOfStock |
/product/tea-tin/?attribute_size=Large | One Offer at 14.00 | https://schema.org/InStock |
The product's own address says InStock as long as one variation can be bought, and its price range still starts at the price of the variation that is sold out. It says OutOfStock only when every variation is out of stock.
"Hide out of stock items from the catalog" reaches this too, late. With the box ticked, the sold-out variation's price left the range only after the variable product was saved again, and the markup then became one Offer at 14.00. WooCommerce's "Clear transients" tool did not bring that forward.
WooCommerce variations and SEO covers the addresses that name a variation and what each one tells a search engine.
When to get help
- Ask your host if you cannot edit
.htaccess, or if the server is nginx and you cannot change its configuration. A redirect plugin does the same job from the dashboard. - Hand it over if a store has many discontinued products and each needs a decision. WP Ministry's WooCommerce SEO service is the one for a store. To start from a written list of what to fix, a WordPress SEO audit is a one-time, written audit of what stands between a WordPress or WooCommerce site and a search engine.
Common questions
Should I delete out-of-stock products in WooCommerce?
Not if they are coming back. A product that is out of stock keeps its address, answers 200 and says OutOfStock, and it says InStock again when you restock it. Deleting it makes the address answer 404, and a product created later is a new page. Delete or trash a product only when it is gone for good.
Does WooCommerce add noindex to a product that is out of stock?
No. The robots tag on the product's page is the same before and after, and the product stays in the sitemap. Ticking "Hide out of stock items from the catalog" does not add one either. That box changes which of your own pages list the product.
Does "Hide out of stock items from the catalog" remove the product from Google?
It does nothing that asks for that. The product's own address still answers 200 and is still in the sitemap. The box takes the product out of the shop, category pages and product search on your own site. Whether Google keeps showing a page is Google's decision.
Should I redirect a discontinued product to the home page or to its category?
Not to the home page. Google's guide to site moves says a redirect to an irrelevant destination, with the home page as its example, might be treated as a soft 404. Its guidance is to redirect when a page has a clear replacement, and to answer 404 or 410 when it has none. A category that happens to hold similar products is a judgment call that Google's documentation does not settle.
What does the address of a product in the trash answer?
404, with the site's "Page not found" page, unless WordPress finds a published post whose slug begins the same way. Then it answers 301 to that post. Run the status command on this page to see which.
How long does Google take to drop a removed product?
Google gives no date. Its documentation says an address that answers 404 is removed from the index, and that Google stops using such an address over time. Search Console's help says Googlebot will probably keep trying a removed address for a while, less and less often.
- ResourceWooCommerce checkout is down: a runbook
- Cost guideWooCommerce maintenance cost: why a store costs more, and what the extra should buy
- ResourceWooCommerce sale readiness checklist: Black Friday or any big sale
- Error fixHow to fix WooCommerce payment gateway errors
- Error fixHow to fix a WooCommerce checkout that is not working
- GuideMismatched value (page crawl) for price in Merchant Center: finding the cause on a WooCommerce store

