Fix library

How to fix soft 404 errors

A soft 404 is a page that tells a human it does not exist while telling a crawler it does. The body says not found, the server answers 200. Google's crawl documentation describes exactly how this gets caught: “If the content suggests an error for Google Search, an empty page or an error message, Search Console will show a soft 404 error.” The fix is to return a real 404 or 410 status code, or to give the page content worth indexing.

The symptom: your error pages are getting indexed

Search Console lists URLs under “Soft 404” in the Page Indexing report. Or worse, nobody checked Search Console and the symptom shows up in the wild: a search result that leads to a “Sorry, we couldn't find that page” screen, or an AI answer that cites a URL on your domain that has been dead for a year. The page looks like an error to every human who lands on it. To the crawler it looked like a perfectly healthy page, because the server said 200.

Status codes are the contract between your server and every machine that reads your site. A 200 means “here is the content you asked for.” When the content is an error message, the contract is broken. Google's Search Console documentation puts the mismatch in one sentence: the page “returns a user-friendly 'not found' message but not a 404 HTTP response code,” per the Page Indexing report reference. Crawlers read the code first and the words second. If the code lies, the crawler wastes budget on dead URLs, keeps them in the index longer than it should, and trusts your other 200s a little less.

The cause: the server says 200 when it means 404

Three patterns produce almost every soft 404 we see.

In each case the root cause is the same. The decision about whether a page exists was made in the wrong layer, or never made at all.

The fix: make the status code tell the truth

Decide what each dead URL actually is, then answer with the matching code.

In an Express app, the error handler goes after every real route, and it sets the code before it sends the friendly page:

// Last middleware: anything unmatched is a real 404.
app.use((req, res) => {
  res.status(404).sendFile("404.html", { root: "public" });
});

// For an SPA, keep the catch-all for real app routes only,
// and let unknown paths fall through to the 404 handler.

For a purely static host, most platforms let you define a custom 404 page that keeps the real status code. Use that mechanism, not a rewrite. The friendly error page and the honest status code are not in conflict. You can have both, and you should.

Be honest: a 404 is not a failure

Site owners avoid 404s because the word “error” sounds like something to hide. So they redirect every dead URL to the homepage and call the report clean. That is the dishonest version of this fix, and it does not work. Google documents mass redirects to irrelevant pages as soft 404s too, and visitors who asked for a product page and got your homepage leave. A URL that no longer exists should say so. That is not a defect. It is the one situation where an error code is the correct, honest answer, and machines reward servers that answer honestly.

Verify: check the code, not the page

Your eyes cannot verify this fix, because the visible page looks identical either way. Check the header:

curl -I https://example.com/some-deleted-page

HTTP/2 404   ← this line is the whole test

Run that against a URL you know is dead. If the first line says 200, the fix is not live. Then confirm in Search Console: use URL Inspection on a formerly soft-404 URL and watch the Page Indexing report over the following weeks as the “Soft 404” count drains.

Then verify it the way the engines will. Paste your link into our GEO audit or the full Brimm audit and we fetch your 404 behavior directly: we request a URL that cannot exist and read the status code your server actually returns. If it says 200, we flag it. For the wider picture on why crawl-level failures keep sites out of AI answers, start with why isn't my site showing up in AI search, and the rest of the fix library covers the other failures we find most.

See what your dead URLs actually return.

Paste your link. We probe a URL that cannot exist and read the status code your server sends back. The preview is free.