Soft 404 Errors: What They Are and How to Fix Them

By SEOgrinder · Published · 10 min read

Short answer: A soft 404 is a page that tells visitors “not found” (or shows almost nothing) but answers with the success status 200 instead of 404. Google treats such pages as errors: when a 2xx response has content that points to an error, Search Console reports a soft 404. The fix is to make the server send the status that matches the truth: 404 or 410 for pages that are gone, a 301 to an equivalent page for pages that moved, and real content for pages that should exist.

What is a soft 404?

Every answer from a web server starts with a status code, and the code comes before any content. 200 means “OK, here is the page”. 404 means “nothing here”. A soft 404 is a mismatch between the two: the page on screen says “not found”, “0 results” or shows nothing worth reading, yet the status is 200.

Google describes the case in its documentation on HTTP status codes and network errors. For a 2xx response, Google takes the content into account. If that content points to an error, for example an empty page or an error message, Search Console shows a soft 404 error. For a genuine 4xx status, Google does not use the content of the URL at all.

You can see the difference in a terminal. On a site with this problem, a request to an address that does not exist looks like this (the address is a placeholder):

$ curl -s -o /dev/null -w "%{http_code}\n" https://your-site.example/page-that-does-not-exist
200

The correct answer is 404. A visitor sees the same friendly “Page not found” text in both cases. Only the status differs, and the status is the first thing a crawler reads.

The word “soft” does not mean “mild”. It means that the server says everything is fine while the page says it is not.

Why does a soft 404 matter?

Here is what is documented, and what is not.

Documented:

  • Search Console lists the URL as a soft 404 error, so Google itself calls it a problem.
  • With a real 404 or 410, Google does not use the content of the URL. With a 200, you leave it to Google to guess from the content whether the page is an error.

Not documented: a direct effect on rankings. Google names the error in its report. It does not say that a soft 404 pulls down other pages of your site, and we found no source that proves it. Treat claims such as “soft 404s destroy your SEO” with suspicion.

Our view, and nothing more than that: a page without value should not be offered as a result, and a fix costs little. There is also a practical side that has nothing to do with Google: link checkers and uptime monitors read status codes. If every address answers 200, they cannot tell you that a link is broken, and visitors who follow an old link are never told that the page is gone.

What causes a soft 404?

Soft 404s typically come from patterns like these:

  • A single-page app that answers every path with the same shell. The server sends the app, with status 200, for any address. Only after the JavaScript has run does the screen say “not found”. The status was decided before that, and it is 200. Google processes such pages in steps (crawling, rendering, indexing), and a page with status 200 goes into the queue for rendering. Google's JavaScript basics explain the model, and our article on AI crawlers and JavaScript covers crawlers that may not run scripts at all.
  • A nice error page with status 200. A CMS, a framework or a proxy rule renders a designed “not found” template but forgets to set the status.
  • Empty list pages. A category, tag or internal search page with no entries still answers 200 and shows “0 results” or an empty frame.
  • Sold-out or discontinued products. The page stays up with little more than “not available”. A page that still carries a real description and a note on availability is a different case from a bare notice.
  • Redirecting everything to the home page. A deleted page that sends visitors to the start page ends in a 200 with unrelated content. The home page is rarely the equivalent of the page that is gone.

Another cause is the thin page: a template with almost no content, such as a placeholder or a half-built section. Content that looks like an error message or an empty page can be flagged even when nobody meant it to be an error page.

How do you find soft 404 errors?

You have three ways, and they complement each other.

  1. Search Console. Open the page indexing report and look for the reason “Soft 404”. It lists the URLs that Google itself has put in this box. The URL inspection tool shows what Google gets from a single address.
  2. A terminal. Request an address that cannot exist and read the status (command below).
  3. The browser. Open the developer tools, switch to the Network tab, load a made-up address and look at the status of the first request. The page itself tells you nothing, because it looks the same with every status.

For the terminal check, the -L option follows redirects, so you also see where an address ends up:

curl -s -L -o /dev/null -w "%{http_code} %{url_effective}\n" https://your-site.example/page-that-does-not-exist

You want 404. If you get 200 https://example.com/, the server redirects unknown addresses to the home page. Use GET like this rather than curl -I, because some servers answer a HEAD request differently.

Test from outside: request the public address, not the application directly. A CDN, a cache or a firewall in front of the application can change the answer.

How SEOgrinder tests for a soft 404

The free scan in SEOgrinder asks your site for an address it makes up, with a random name that cannot exist. If the answer is 200, it reports a finding. A redirect from that address to your home page counts as the same finding, because it also ends in 200. The result appears in the “Page on Google” tab, in the “Indexing” card, as a line: “Unknown addresses return 404 correctly”, or “Unknown addresses return 200 instead of 404”. The finding goes into the task list with advice to return a genuine 404 or 410.

Two honest limits. First, this test asks how your server handles addresses that do not exist. It does not tell you which of your real URLs Google has filed as a soft 404. For single URLs, the verdict is in Search Console. In a local installation that is connected to Search Console, SEOgrinder also reads the stored index status of the scanned page and says so when Google lists it as a soft 404. Second, the test cannot tell you whether a thin page of yours looks empty to Google. The site crawl does flag thin pages and broken internal links, which are the places where the problem often starts.

Which status code is right? A decision table

The right answer depends on what is true about the page. Change the status to match the truth, not the other way round.

SituationRight answerNote
The page is gone for good, with no equivalent404 or 410A designed error page is fine, but it must send the error status
The page moved to a new address301 (or 308) to the equivalent new pageA strong signal that Google should process the target
The move is truly temporary302 (or 307)A weak signal, so not for permanent moves
The page exists but is thin or almost emptyAdd real contentOr remove it, if it has no purpose
A list or search page that is empty for good404Do not offer it in links or the sitemap
A list page that is empty for nowShow something usefulFor example related items and a note, with status 200
A product that is gone for good404 or 410, or a 301 to its direct successorNot to the home page
A product that is out of stock for a whileKeep the page with its real contentStatus 200 is correct here

Google treats 301 and 308 as a strong signal that the redirect target should be processed, and 302 and 307 as a weak one. It follows up to ten redirect steps by default and ignores the content of the URL that redirects. So redirect once, directly to the final address.

How to fix a soft 404 in nginx, Node and single-page apps

nginx

This configuration serves a designed error page and keeps the status 404. The line error_page 404 /404.html; keeps the original status unless you add =200, so do not add it.

server {
    root /var/www/example;
    error_page 404 /404.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location = /404.html {
        internal;
    }

    # removed on purpose
    location /old-campaign/ {
        return 410;
    }

    # moved
    location = /old-page {
        return 301 https://example.com/new-page;
    }
}

Node

For routes with data, the code has to look up the record first and decide the status afterwards. This example uses only Node's own modules:

import http from 'node:http';

const products = new Map([['blue-mug', 'Blue mug']]); // stand-in for your database

http.createServer((req, res) => {
  const { pathname } = new URL(req.url, 'http://localhost');
  const match = /^\/products\/([a-z0-9-]+)$/.exec(pathname);
  const name = match && products.get(match[1]);
  res.writeHead(name ? 200 : 404, { 'content-type': 'text/html; charset=utf-8' });
  res.end(name ? `<h1>${name}</h1>` : '<h1>Page not found</h1>');
}).listen(3000);

The error page can be friendly and have a design, and the status is still 404. Most frameworks have a “not found” function or hook for the same purpose. Look for the one that sets the status and does not only render a template.

Single-page apps

The server must know which paths are real. The common shortcut, “send index.html for everything”, is exactly what creates the problem. In nginx you can list the real routes and answer 404 for the rest:

location = / {
    try_files /index.html =404;
}

location ~ ^/(about|contact|docs)/?$ {
    try_files /index.html =404;
}

location / {
    try_files $uri =404;
}

Assets such as scripts and images are found by the last block, and every other path gets a 404. If the route list is long or changes often, generate it from your router at build time. The alternative is to render real pages on the server or in advance, so that each address has its own HTML file and the server can answer for it. Google itself calls server-side rendering or pre-rendering a good idea, because it makes the page faster for users and crawlers and because not all bots run JavaScript. For apps where setting a status code is impossible or impractical, Google's JavaScript basics name two more ways: redirect with JavaScript to an address that answers 404 (for example /not-found), or add <meta name="robots" content="noindex"> with JavaScript on error pages.

After every change, run the curl check again and test one real page, one moved page and one address that cannot exist.

Common mistakes

  • Fixing the design, not the status. A beautiful “not found” page that sends 200 is still a soft 404.
  • Redirecting all missing pages to the home page. It hides the problem from your own tools and gives visitors something other than what they asked for. Redirect to an equivalent page, or answer 404.
  • error_page 404 =200 /index.html; and similar shortcuts. They change the status to 200 for every unknown address.
  • Testing only in the browser. The browser looks the same for every status. Read the status.
  • Blocking the addresses in robots.txt instead of fixing the status. A crawler that may not fetch a page never sees its status code.
  • Leaving dead addresses in the sitemap. A sitemap should list only pages that exist and answer 200.
  • Expecting the report to clear at once. The Search Console entry changes when Google fetches the URL again. Nobody can promise when that happens.

FAQ

Does a soft 404 hurt my rankings?

Not proven. Google reports it as an error in Search Console, which is documented. A direct ranking penalty for the rest of your site is not documented in any source we used. The sensible reading is: fix it because it is a wrong answer, not because of a promised gain.

Is a soft 404 the same as a 404?

No. A 404 is an honest answer: the status says the page does not exist. A soft 404 says 200 while the content says the opposite.

Should I send 404 or 410?

Both are 4xx error statuses, and Google does not use the content of the URL for either. 404 is the usual answer for “not found”. 410 says “gone”, so it fits pages you removed on purpose.

Can I have a custom 404 page?

Yes, and you should. Design it as you like, add a link to the home page or a search, but make sure the status is 404.

How do I check my whole site?

Run the free scan to test how your server answers unknown addresses, and look at the Search Console report for the URLs Google has flagged. Check one more thing by hand: pages that carry nothing but a short notice.

Your next step

Run curl against an address that cannot exist on your own site. If you do not get 404, fix that first. Then run the free scan with SEOgrinder, find the soft 404 line in the “Indexing” card of the “Page on Google” tab, and press “Check again” after your change to see the new result.

More from the blog