lintpage
~/search-console/soft-404
§ page indexing report
blocking

"Soft 404" in Search Console

Your server says 200 and your page says the content is gone. Google believes the page, not the status code, and refuses to index either.

This URL is not indexed and it should be. Something is in the way.

§ check your url

Is it still happening?

Paste the URL Search Console flagged. We fetch it, read its status, title, heading, and how much text it actually contains, and separately request a URL we invent on the same host to see whether your server can return a 404 at all.

what this proves

Whether the status code contradicts the page content, and whether your server returns 404 for URLs that do not exist.

what it cannot prove

Google also classifies a page as a soft 404 when the content is too thin to be worth indexing, which is a judgement about substance rather than a signal we can measure. A clean result here means the mechanics are right, not that the page is necessarily substantial enough.

§ what google is telling you

What this error actually means.

Google requested this URL, got a 200 OK, and then decided the response was not a real page. Either it announced itself as missing, or there was too little on it to be worth indexing.

The mismatch is the entire problem. A status code is a machine-readable claim about whether content exists, and a soft 404 is a page making that claim in one place and contradicting it in another. Google resolves the contradiction by trusting what it can read.

This is one of the few rows where the cause is often site-wide rather than page-specific. A single-page-application router or a catch-all route that renders an error view without changing the status will produce a soft 404 for every dead URL on the site at once.

§ causes

6 things that produce this error.

Ordered by how often they turn out to be the cause, not by how obvious they look.

01

An error page served with a 200 status

The page says "not found" and the server says everything is fine. Common in single-page applications, where routing happens in the browser after a 200 has already been sent, and in frameworks where a custom error template was wired up without changing the status code.

text
HTTP/2 200          <- what the server said
<h1>Page not found</h1>   <- what the page said
02

A catch-all route that renders something for every path

Any unmatched URL returns the app shell or the homepage with a 200. Google can invent a URL, receive a valid response, and conclude that your site never signals a missing page. This is the version worth testing for first, because it affects every dead URL you have.

03

An empty listing page

A category with no products, a tag with no posts, a search results page with no matches, a filter combination that returns nothing. The page is technically correct and has nothing on it, so Google reads it as an empty shell rather than content.

04

Content that requires JavaScript that failed or was blocked

The HTML arrives nearly empty and the content is fetched client-side. If the script errors, times out, or its bundle is blocked in robots.txt, Google renders a page with no content and classifies it accordingly. The page looks fine in your browser because your browser succeeded.

05

A page behind a paywall or a login that renders a stub

A 200 that shows a prompt instead of content. Without structured markup describing the paywall, Google sees a page whose entire body is an invitation to log in.

06

A product or listing that expired without a redirect

The item is gone, the template still renders, and the page now consists of a heading and an apology. Common on e-commerce and job boards, where the URL should either return 410 or redirect to a genuine replacement.

§ the fix

How to clear it.

step 1

Test whether your site ever returns a 404 at all

Request a URL you are certain does not exist. If it returns 200, that is the root cause and it affects every dead URL on the site, not just the one Google flagged. Fix it once and the whole report row usually clears.

bash
curl -sI https://example.com/this-page-cannot-possibly-exist-9f2a | head -1
# Want: HTTP/2 404
# Bad:  HTTP/2 200
step 2

Return the status that matches the content

A page that says content is missing must return 404, or 410 if the removal is permanent and you want it dropped faster. Keep the friendly error design; only the status line needs to change. In a single-page application this means the server has to know which routes exist rather than deferring every decision to the client.

ts
// Next.js App Router
import { notFound } from 'next/navigation';

const post = await getPost(slug);
if (!post) notFound();  // renders not-found.tsx with a real 404
step 3

Give empty listings something, or take them out of the index

An empty category should either show related content and stay indexable, or return 404 and stop being advertised. The one thing not to do is serve an empty 200 and keep it in the sitemap, which is what produces this row on most e-commerce sites.

step 4

Redirect expired content to a real successor

A sold-out product with a natural replacement should 301 to it. One with no replacement should return 410. Redirecting everything to the homepage instead is treated as a soft 404 of its own, so it swaps one row of this report for another.

§ faq

Questions, answered.

What is the difference between a 404 and a soft 404?
A 404 is a server telling Google the page does not exist, which Google accepts immediately and stops re-checking. A soft 404 is a server saying 200 while the page says otherwise. Google has to work out which to believe, keeps retrying the URL, and declines to index it. The hard 404 is the correct and cheaper outcome.
Should I use 404 or 410?
Use 404 by default. Use 410 when you are certain the content is gone permanently and you want it dropped faster. Google treats 410 as a stronger signal and removes the URL from the index sooner, but the practical difference is modest and 404 is safer if there is any chance the page comes back.
Why is my empty category page reported as a soft 404?
Because from Google's side an empty category and a missing page look identical: a 200 with almost nothing on it. Either fill the page with something useful, such as related categories or recently sold items, or return a 404 and remove it from your sitemap. Serving an empty 200 and continuing to advertise it is the combination that produces this row.
Does a soft 404 hurt the rest of my site?
Indirectly. Each soft 404 consumes crawl budget Google would otherwise spend on real pages, and it keeps re-checking because a 200 implies the page is legitimate. On a large site with a catch-all route, this can be tens of thousands of URLs and it does slow down discovery of everything else.
Can a JavaScript-rendered page cause this?
Yes, and it is a frequent cause. If the initial HTML is nearly empty and the content arrives via client-side fetches, Google has to render the page before it sees anything. If the script fails, times out, or its bundle is disallowed in robots.txt, what Google indexes is the empty shell. Check that your JS and CSS are crawlable and compare the raw HTML against what your browser shows.
§ before the next deploy

Catch this one before Search Console does.

Search Console tells you weeks after the fact. LintPage runs 60 checks against a URL in about 30 seconds. Free, no signup.

run a full scan →