Soft 404s: what they are, how to find them and how to fix them
A soft 404 is a page that tells visitors it doesn't exist, or is nearly empty, while the server answers 200 OK. Google then has to work out from the content that it's an error. When it does, Search Console lists the page as "Soft 404" and leaves it out of results. The fix is to make missing pages return a real 404 or 410, redirect moved pages to their replacement, and give real pages enough content that they don't look empty.
What a soft 404 looks like
Visitors and search engines read two different things. People read the page. Crawlers read the status code first:
It may have moved or been removed.
Back to the shopHTTP/1.1 200 OK ← "this page is fine" content-type: text/html; charset=utf-8 cache-control: max-age=600 A real missing page should start: HTTP/1.1 404 Not Found
The words say "missing", the status says "fine". Google may spot the error from the words, or it may index an empty page.
A real 404 is clear-cut: Google doesn't index a URL that returns a 4xx status (404, 410 and the like), drops it if it was indexed, and crawls it less over time. A soft 404 depends on Google spotting the error from the content. It also won't show up in any report that lists broken pages by status code.
Where they come from
What each page should return
| Situation | Return | Why |
|---|---|---|
| Gone, no replacement | 404 or 410 | Tells search engines to drop it. 410 says it's gone on purpose, though Google treats 404 and 410 the same. Keep a helpful not-found page for visitors: the status and the design are separate. |
| Moved, or a close replacement exists | 301 to the replacement | Sends visitors and links on to the right place. Only redirect to a page that really replaces it. |
| Product out of stock for now | 200, with real content | Keep the details, say when it's back, and show alternatives, so the page is still worth indexing. Say "out of stock", not "no longer available", in the title and heading. |
| Empty listing that will fill again | 200 with noindex, or 404 | Keep it out of results until there's something to show. |
| Valid page that Google flags | 200, with more content | Google can mistake a very thin page for an error. Add the content the page is meant to have. |
Thin pages
A page doesn't have to say "not found" to be read as one: an empty or nearly empty page can be flagged too. There's no official word count, but a main content area with only a few sentences deserves a look. Contact, login and basket pages are fine short. Pages meant to rank for something usually aren't.
How to check by hand
1Test what a missing page returns 30 seconds
The quickest test for a whole site: ask for a URL that can't exist. Open DevTools (F12) on any page of the site, go to the Console and paste:
// Asks the server for a page that can't exist, and shows the status it returns
const res = await fetch(location.origin + '/this-page-should-not-exist-' + Date.now(), { cache: 'no-store' });
const s = res.status;
console.log(s === 404 || s === 410 ? 'Good: missing pages return ' + s
: s >= 400 && s < 500 && s !== 429 ? 'OK: missing pages return ' + s + ' (Google treats it like a 404, but 404 or 410 is clearer)'
: s >= 500 || s === 429 ? 'Server error ' + s + ': not a soft 404, but fix the error first'
: 'Soft 404: a missing page returned ' + s + (res.redirected ? ' after redirecting to ' + res.url : ''));
If it returns 200, or redirects to the homepage, missing URLs on your site are probably soft 404s. Try a made-up URL inside each section too, such as /products/not-a-real-product, because product and blog templates often handle missing items their own way. To find links that already lead to missing pages, see how to find broken links.
2Check a page that says it's missing 30 seconds
On a page you suspect, such as a discontinued product, paste:
// Checks this page: does it say "not found" while the server says it's fine?
const res = await fetch(location.href, { cache: 'no-store' });
const h1 = document.querySelector('h1');
const text = document.title + ' ' + (h1 ? h1.innerText : '');
const saysMissing = /(page|product|item|post)s? (not found|can['’]?t be found|cannot be found)|^not found\b|(does not|doesn['’]?t) exist|no longer (available|exists)|\b(error 404|404 error|404 not found|404 page)\b|^\s*404\s*$/i.test(text) || /^\s*404\b/.test(document.title);
console.table({
'HTTP status': res.status,
'Says it is missing': saysMissing ? 'yes' : 'no',
Verdict: !saysMissing ? 'No not-found wording in the title or H1'
: res.ok ? 'SOFT 404: says missing but returns ' + res.status : 'OK: returns ' + res.status,
});
It reads the page's title and first H1 for not-found wording, and checks the status the server sends.
It only looks for common English phrases in the title and first H1, so an article about 404 errors won't trip it, but neither will a page that just says "Oops!" or is empty. Check those yourself.
3Read the status in DevTools 1 minute
Open the Network tab, reload, and look at the Status column for the first row (the page itself). A not-found page should show 404, not 200.
4See what Google flagged 5 minutes
In Google Search Console, open Indexing → Pages and look for Soft 404 under the reasons pages aren't indexed. Open it to see example URLs, then decide for each one whether it's really missing (return 404 or 410) or real but thin (add content).
If Search Console lists the page as crawled but not indexed, see Crawled and Discovered, currently not indexed.
How crawlers report this
If a crawl sent you here, these are the labels to look for. Our guide to Screaming Frog and Sitebulb issues covers other common ones.
| Screaming Frog | Sitebulb | What it means |
|---|---|---|
| Content: Soft 404 Pages | Contains possible soft 404 phrases | The page returns 200 but reads like an error or empty page. |
| Content: Low Content Pages | No direct match | The page has little text. This is a prompt to look, not a fault in itself. |
Doing this on every page?
SEOSnip spots soft 404s for you
Open the extension on any page and it compares what the page says with the status the server sent. You get:
- A flag when the title or first H1 uses not-found wording such as "page not found", "product not found", "no longer available" or "error 404" while the status is 2xx, with the words highlighted
- A flag when the main content (or the whole page, if there's no single
<main>) has fewer than 200 words, as a prompt to look rather than a rule - A ticket with that evidence, and a "Check if fixed" button that re-runs the check after the change ships
Page looks like a soft 404
| In the <h1> |
|---|
| Sorry, this product is no longer available |
| Server responded HTTP 200 · a missing page should return 404 or 410 |