What happens if you never unpublish a sold-out product on Shopify
Key takeaways
- We opened 65 sold-out products across 13 live Shopify storefronts on 26 August 2026. All 65 returned HTTP 200 — the page loads normally, exactly like an in-stock one. All 65 were still listed in their own store's sitemap, the file the store hands to search engines. None carried a
noindex, either in the page or in the response header. - This is the platform's default, not anyone's oversight. Selling out changes whether a product can be bought. It does not change whether it is published, and the sitemap is built from what is published.
- The pooled percentage everyone would screenshot is the one we trust least. Across these 13 stores 1,921 of 13,583 published products were sold out — but one store supplies 63% of that numerator. The per-store median is 12.9%, and we picked these thirteen because they had sold-out products, which inflates every figure here. The per-store table is below.
- The four options below are not ranked. Three of them cost you the same thing — the URL — and the differences between those three are about how much review you get, not how much you pay.
- The right answer is often to leave it published, and you can check your own store in one paste, from a browser, with nothing installed.
Check your own store first
This uses two files every Shopify storefront serves publicly: the products endpoint and the sitemap. It only reads — every call in it is a GET, there is no method, no body, and no write anywhere in it.
Open your storefront (not the admin) in a browser, open the developer console (⌥⌘J on a Mac, Ctrl+Shift+J on Windows), and paste:
// 1. published products where every variant is unavailable
const gone = new Set(); let total = 0;
for (let page = 1; ; page++) {
const r = await (await fetch(`/products.json?limit=250&page=${page}`)).json();
if (!r.products.length) break; // stop on an EMPTY page, never on a short one
total += r.products.length;
for (const p of r.products)
if (p.variants.length && p.variants.every(v => v.available === false)) gone.add(p.handle);
await new Promise(done => setTimeout(done, 300)); // be polite to the store
}
// 2. product URLs your store is currently handing to search engines
const locs = u => fetch(u).then(r => r.text())
.then(t => [...t.matchAll(/<loc>\s*([^<\s]+)/g)].map(m => m[1]));
const listed = new Set();
for (const shard of (await locs('/sitemap.xml')).filter(u => u.includes('products')))
(await locs(shard)).forEach(u => listed.add(u.split('/products/')[1]?.split('?')[0]));
// 3. the overlap
const both = [...gone].filter(h => listed.has(h));
console.log(`${gone.size} of ${total} published products are fully sold out. ${both.length} are still in your sitemap.`);
console.table(both.map(h => ({ product: h, url: `/products/${h}` })));
This is not quite the instrument that produced our table. Ours stopped at 5,000 products and at twelve sitemap shards per store, because it had to run across many stores in one pass; yours has neither cap, so on a large catalogue it will read further than ours did. It also has a 300ms pause ours did not need.
If nothing happens, your browser may want you to type allow pasting first. If it throws before printing anything, the most likely cause is that the storefront is password-protected, in which case that endpoint returns an HTML page rather than JSON and the parse fails — the other common cause is running it in the admin instead of on the storefront. And if you write your own version, stop paging on an empty page, never on "fewer results than I asked for" — that endpoint returns short pages with more behind them, and the version that got this wrong read a 3,100-product catalogue as 238 products, a 13x undercount with no error and no warning.
What the list is for. You now have handles, not a task. The decision each one needs is is this coming back? — and that is the one thing none of this can tell you, because nothing visible from outside a store says how long a product has been unbuyable. Sort your list by whether you would reorder the item. That split, not the count, is what maps onto the four options below.
What we checked, one product at a time
| What we checked | Sold-out products where it held |
|---|---|
/products/<handle> returns HTTP 200 |
65 / 65 |
| The handle is still in the store's own sitemap | 65 / 65 |
Page carries a <meta name="robots"> tag of any kind |
0 / 65 |
Response carries an X-Robots-Tag header |
0 / 13 stores |
canonical points at the product itself |
65 / 65 |
| The page says "sold out" or "out of stock" in the HTML | 50 / 65, in 10 of 13 stores |
Structured data declares OutOfStock |
60 / 65, in 12 of 13 stores |
Structured data has no availability field at all |
5 / 65, all in 1 store |
The first two rows are less of a discovery than they look. We defined a sold-out product as one the public endpoint still returns, which means still published — and a Shopify sitemap is generated from what is published. So "it loads" and "it is in the sitemap" largely follow from how we picked the products. They are worth measuring because a lot of advice implies otherwise, but they are not the interesting rows.
Two rows are. Fifteen of the sixty-five pages never say "sold out" or "out of stock" anywhere in the HTML we fetched, and five carry no availability field in their structured data. Both cluster by store, not by product: the fifteen come from exactly three stores, five products each, and the five from one store. That makes them properties of a theme — one check for your whole store rather than one per product. We cannot say whether those three stores render the wording in JavaScript after the page loads; we read what the server sent.
Google's merchant listing documentation defines https://schema.org/OutOfStock as "The item is currently out of stock" and notes that "The short names without the URL prefix are also supported" (source). We looked for a sentence in that documentation telling you to remove or hide out-of-stock pages, and there isn't one.
One caveat about how we read that field: we matched the raw HTML rather than parsing the JSON-LD, so what we saw included http://, https:// and a JSON-escaped http:\/\/ form. The escaping is an artefact of reading the page as text — the same value, not a third way of declaring it.
How much of a catalogue this is, per store
The pooled figure is the one that travels, so here is what it hides.
| Store | Published | Fully sold out | Share |
|---|---|---|---|
| A | 2,693 | 1,207 | 44.8% |
| B | 2,676 | 40 | 1.5% |
| C | 2,375 | 86 | 3.6% |
| D | 1,503 | 13 | 0.9% |
| E | 983 | 171 | 17.4% |
| F | 910 | 21 | 2.3% |
| G | 901 | 53 | 5.9% |
| H | 377 | 60 | 15.9% |
| I | 311 | 51 | 16.4% |
| J | 310 | 40 | 12.9% |
| K | 213 | 13 | 6.1% |
| L | 176 | 84 | 47.7% |
| M | 155 | 82 | 52.9% |
| Pooled | 13,583 | 1,921 | 14.1% |
| Median store | 12.9% | ||
| Median store | 10.3% |
Store A supplies 63% of the pooled numerator on 20% of the products. Drop it and the pooled share is 6.6%. That is why the median matters more than the pool here, and why we would not put 12.9% in a headline.
And both numbers are inflated by construction. We chose these thirteen from a larger set precisely because they had the most sold-out products in it. Every store in the table therefore has at least one by construction. If you want a share that includes stores at zero, this is not that number.
Why the default is what it is
Shopify separates two things merchants tend to think of as one: whether a product is published to a sales channel, and whether it is available to buy. Selling the last unit changes the second and leaves the first alone. Nothing infers that a zero-stock product should stop being published, because for most catalogues that inference would be wrong — sizes sell out and come back, and spending the URL every time would be worse than leaving it.
The sitemap follows from the same thing. It is generated from what is published, and the product is still published. There is no separate "sold out" state for it to react to.
Option 1: change what shoppers meet, not what exists
Nothing here touches the product itself. An automated collection can be built on a condition over inventory stock so that only in-stock products fall into it, and sold-out items drop out of the collection page while their own URLs stay exactly as they are. Search & Discovery, which is a separate app you may or may not have installed, can also sort out-of-stock products to the end of a product list.
⚠️ Two limits, both of which we hit ourselves. This only applies to automated collections — if yours are manual, there is no condition to set and this option does not exist for you. And we are describing the mechanism rather than quoting the dropdowns, because we do not have a store admin open and Shopify's smart-collection help pages do not list the exact condition wording. You will see the real labels in your own admin; we are not going to guess them at you.
This costs nothing and is reversible, and it does not change a single row of the table above: the page stays live and stays in the sitemap. If your goal was merchandising, this is the whole answer. If your goal was the sitemap, this does not touch it — and nothing short of unpublishing does, which is the honest answer to a question a lot of articles leave open.
Option 2: Shopify Flow's Unpublish product action
Flow can unpublish automatically. The documentation describes the action plainly: "The Unpublish product action makes the product that triggers the workflow unavailable on the selected sales channels" (source). The same page notes that the action's Product ID field "is set by default and can't be changed" — it always acts on the product whose event fired the trigger, never on a different one.
The cost is the URL. An unpublished product is no longer served, so whatever the page had accumulated in links and search history stops being reachable, and anyone arriving from a search result or an old email gets a 404.
🔴 Two things to set up before you turn this on, in this order. First, a condition on how long stock has been at zero — without one, this fires on every temporary stockout, including the size that comes back Thursday. Second, the redirect. Flow's Unpublish action does not create one. If you build the workflow and stop there, every automatic unpublish produces exactly the gap this article's own advice warns against: the URL dies now, the redirect arrives whenever you next think about it. Shopify's URL redirects are the tool, and they have to exist first.
Option 3: CSV export and re-import
Export products, set the Published column to FALSE for the handles you want gone, re-import. This is the option for when you want to look at the list and decide product by product.
The cost is the same URL cost as Option 2 — including the same redirect requirement, which a CSV import will not create for you either — plus the risk of a bulk write you cannot preview. Take an export of the current state first and keep it; that export is your only way back.
⚠️ We have not run a CSV import to verify the failure modes, so we are not going to list them as though we had. What we can tell you is the shape of the risk: the import is applied rather than previewed, and it is applied to whatever your file says, including columns you did not mean to touch. Before you do this on a real catalogue, read Shopify's own import documentation on how columns are interpreted, and try it on two products before you try it on two hundred.
Option 4: describe the rule and review the diff
This is our own product, so weigh the rest of this section accordingly.
Arvio is an AI store operator for Shopify. For this job the useful part is not that it can unpublish products — Flow does that. It is that you can ask what changed across your catalogue, review every edit before it goes live, and undo anything. The CSV route's real cost is committing to a bulk write you have not seen the result of; a review step removes that cost and nothing else. It does not remove the URL cost, and it does not create your redirects.
We are not showing you a screenshot of that review step in this article, so treat the paragraph above as a claim rather than as evidence — everything else here you can re-run yourself, and this you cannot.
When you should not use us for this. If your rule is simple and stable — unpublish anything at zero stock for 30 days — Flow is the better fit: it runs unattended and needs no review step, because the rule is not in question. A review step earns its place when the decision is per-product judgment rather than a rule, which is usually the first pass over an old catalogue, not the maintenance afterwards. Flow is available on the standard Shopify plans, so for most readers this is a real alternative rather than a rhetorical one.
When leaving it published is the right call
Four situations where the table at the top describes exactly what you want, and the correct action is none.
It is coming back. A size that restocks monthly should keep its URL. Unpublishing and republishing repeatedly is strictly worse than leaving it alone.
People are still arriving on it. A sold-out page that still gets visits is telling you about demand, and a back-in-stock signup is the thing that captures it — which it can only do on a page that still resolves. If you have never looked at what those pages get, look before you unpublish them, not after.
It is discontinued and people still search for it by name. The page is the only thing that can send that person to the replacement. Unpublish it and they get a 404, and a 404 recommends nothing.
You have not measured it yet. Run the paste at the top.
Method, and what this cannot tell you
Where the stores came from, plainly. The sampling frame is the public storefronts of merchants who have installed our app. We did not ask them, and we did not use anything from inside the app: every figure here came from /products.json, /sitemap.xml and the product pages themselves, all of which are open to any browser on any Shopify store, including yours and ours. But the list of which stores to read is knowledge we have because of a commercial relationship, and no reader could reproduce our exact sample. That is a real limit on this study and it is the reason the stores are unnamed here.
The funnel. These thirteen come from the 43 storefronts in our catalogue survey, themselves drawn at random from a public list of stores whose owners posted their own URL on the Shopify Community's Store Feedback board. From those 43 we took the ones with the most fully sold-out products, which is why every figure about how much is sold out is high by construction — the interesting measurements here are about what happens to a sold-out page, and those do not depend on how we chose the stores.
Collection. On 26 August 2026 we re-read each store's catalogue in full, then sampled sold-out products at even intervals through the list rather than taking the first few. Nine stores contributed 5 products, two contributed 2, one contributed 1 — 50 in total. Each was fetched individually and checked against the store's sitemap. The response-header check was a separate HEAD request per store, one product each, so that row is out of 12 rather than out of 50.
Definitions. "Fully sold out" means every variant carries available: false and the product is still returned by the public endpoint. Quantities are invisible from outside a store; the only observable thing is whether a shopper can buy.
Two traps we handled. Paging stops on an empty page, never on a short one. And structured data is matched against the whole page, not the first N characters: the 50 sold-out product pages in this sample had a median size of 432 KB and the JSON-LD is often a long way down, so an earlier scan reading only the first 200,000 characters reported roughly a third of the true count.
What it cannot tell you. Not Google's index — we measured what stores publish, and nothing here came from Google, Search Console or any ranking tool, so nothing here is evidence about traffic or revenue. Not intent — we cannot tell a deliberately-kept seasonal page from a forgotten one, and that difference matters more than the count does. Not duration — nothing visible from outside says how long a product has been unbuyable, which is the variable that actually decides what you should do. And not the theme, the plan, or anything else that would let you place your own store among these twelve.
FAQ
Should I unpublish out of stock products on Shopify?
Only if the product is not coming back, and only after the redirect exists. Unpublishing removes the URL along with whatever links and search history the page had earned; for a temporary stockout that buys you nothing. For a genuinely discontinued product it is the right move, with a redirect to the nearest replacement so the demand lands somewhere.
How do I hide out of stock products without unpublishing them?
At collection level. An automated collection built on an inventory condition keeps sold-out products off the collection page, and Search & Discovery can sort them to the end of a list. Both change what a browsing shopper meets while leaving the product's URL untouched. Neither works if your collections are manual.
Does Shopify automatically remove sold out products from the sitemap?
No. All 65 sold-out products we checked on 26 August 2026 were still listed in their own store's sitemap. The sitemap is generated from what is published, and selling out does not unpublish anything.
Do sold out product pages hurt SEO?
We did not measure that, and we are not going to tell you either way from data we do not have. What we can say is that the pages are not misrepresenting themselves — 60 of 65 declared OutOfStock, the value Google's documentation defines for an out-of-stock item — and that Google's documentation contains no instruction to remove such pages. If you need an answer about rankings, this article is not it, and neither are the eight pages currently ranking for this query, none of which measured anything.
Can Shopify Flow unpublish a product automatically when it sells out?
Yes, and it will not create a redirect for you. Build the redirect first, then the workflow, and put a duration condition on it so it does not fire on every temporary stockout.
What happens to the URL after I unpublish a product?
It stops being served, so anyone arriving from a search result, a saved link or an old email lands on a 404. Shopify's URL redirects are the tool for that, and doing it in the other order means the gap is live traffic hitting nothing.
How can I tell whether my theme outputs availability in structured data?
Open a product page, view source, and search for availability — you should find it inside a JSON-LD block. One of the thirteen stores here had none on any product we opened, so this is worth confirming once rather than assuming.
Is my catalogue really readable by anyone?
Yes, and so is every other Shopify store's, ours included. The products endpoint and the sitemap are public by design. That is a fact about the platform worth knowing in both directions: it is how we measured these twelve stores from the outside, and it is why anyone — a competitor, a researcher, you — can do the same to yours without telling you.
Published 26 August 2026 by Adot Technologies Inc, the team behind Arvio: AI Store Operator. Every figure here was collected on 26 August 2026 from public storefront endpoints. How we chose which stores to read, and the limits that puts on the result, are set out in the Method section rather than summarised here.
