Soft 404: unknown URLs return 200, how to fix it
A soft 404 is a page that looks like an error but returns HTTP 200. Your site has the problem when a made up URL answers 200. The cause is a catch all route or a redirect to the home page. Return status 404 for paths that do not exist. The change takes 10 to 30 minutes.
By SeenLens · Last updated
Run the free 43-check scan on your page
What this check tests
SeenLensBot requests one made up path at the root of your host. The path starts with claude-seo-404-probe- followed by random characters, so you may see it in your logs. It follows redirects.
| Final status | Result |
|---|---|
| 200 | Warn: a catch all answers every path |
| 404, 410 or another 4xx or 5xx status | Pass |
| 202, 403, 429 or 503 | Not tested: a firewall may have answered |
A redirect from unknown URLs to the home page ends in 200, so it counts as a catch all. A catch all also makes files such as llms.txt look present when they are not.
What is a soft 404?
Google defines a soft 404 as a page that returns a success code (2xx) while its content suggests an error, such as an empty page or an error message. Search Console then shows a soft 404 error. Google treats real 404 and 410 answers the same: it removes a previously indexed URL from the index and slowly crawls it less often. A 2xx answer does not guarantee indexing.
How to confirm the problem
curl -sIL -o /dev/null -w "%{http_code}\n" https://example.com/this-page-does-not-exist-12345The command must print 404 or 410. It printing 200 is the problem.
How to fix it
Find the catch all rule and make it return 404 for paths that have no page.
# Nginx: return 404 when no file matches
location / {
try_files $uri $uri/ =404;
}
# Not this (every unknown path becomes the app shell with status 200):
# try_files $uri $uri/ /index.html;- Apache: remove a catch all
RewriteRulethat maps every path toindex.html, or limit it to known app routes. UseRedirect gone "/old-page"to return 410 for a removed page. - Next.js (App Router): unmatched URLs show the root
app/not-foundpage with status 404. In a route, callnotFound()when the record does not exist. Before streaming starts Next.js returns 404. After streaming starts the status stays 200, so callnotFound()before you stream content (docs updated 2026-10-06). - Vercel: remove a
rewritesentry invercel.jsonthat sends every path to one page. - Single page apps: Google advises redirecting to a server side 404 URL or adding a
noindextag to the error view. The better fix is a server that returns 404. - WordPress and Shopify: both return 404 for unknown URLs by default. Look for a plugin, app or redirect rule that sends unknown paths to the home page.
Redirect a removed page to a close replacement with a 301 only when a good match exists. Otherwise return 404 or 410.
How to verify the fix
- Run the curl command again. Expect 404.
- Open the page in a browser and check that the error page helps visitors, with a link to the home page.
- Check that your real pages still return 200.
Does a soft 404 hurt my site?
Does a 404 error mean I was blocked?
No. A 404 means the server found no page at that URL. A block usually shows 403, 429 or a challenge page. See the firewall fix for those.
How long does a 404 error last?
It lasts until you publish a page at that URL. Google gradually crawls a URL that returns 404 less often and drops it from the index.
When can you ignore it?
Rarely. A catch all that serves a real page for every path is a design choice for some single page apps, but it creates soft 404s and breaks discovery files. Plan to move the 404 decision to the server.
Sources
- Google Search Central: HTTP status codes, network and DNS errors (soft 404 definition, 404 and 410 handling, checked 2026-11-06)
- Google Search Central: soft 404 errors (checked 2026-11-06)
- Google Search Central: JavaScript SEO basics (single page apps and soft 404s, checked 2026-11-06)
- Next.js: not-found.js (404 status and streaming, updated 2026-10-06)
- Apache: mod_alias Redirect (gone keyword returns 410, checked 2026-11-06)
- nginx: ngx_http_core_module (try_files, checked 2026-11-06)