410-vs-404-status-codes-for-seo.html
< BACK TO BLOG

410 vs 404 status codes for SEO: when each tells Googlebot something different

Google treats 404 and 410 differently, and the difference matters when you are retiring URLs in bulk. The short version: 404 says "missing, try again later"; 410 says "gone, never coming back". Googlebot revisits 404s for weeks before dropping them from the index. It drops 410s in days. If you are mid-migration and looking at thousands of URLs that should disappear forever, picking the right code shaves weeks off the cleanup.

What each code actually means

404 Not Found is the default response when a resource does not exist at the requested URL. The server is saying "this URL has no resource right now". The implication is permissive: maybe the URL will have a resource later, maybe it is a typo, maybe the resource moved and someone forgot to ship a redirect. The 404 is a wide net.

410 Gone is the explicit retirement code. The server is saying "this URL had a resource, it has been permanently removed, do not ask again". It is the closing-door signal. Google documents this explicitly: 410 is interpreted as a stronger signal to drop the URL from the index than 404.

How Googlebot treats each one

404 behaviour. Googlebot encounters a 404, marks the URL as missing, but keeps it in the crawl queue. The bot revisits the URL on a slowing schedule (24h, then 7d, then 30d, then 90d). If the URL eventually returns a 200 again, it is reinstated. The cycle continues for months. The URL stays in Search Console's Coverage report under "Not found (404)" for the duration.

410 behaviour. Googlebot encounters a 410, marks the URL as permanently retired, drops it from the index within days. The URL appears briefly in Search Console under "Page removed because of legal complaint" or "Soft 404" depending on Google's interpretation, then disappears. The crawl queue stops revisiting it.

The functional difference is days vs months of index pollution.

When to pick 410 over 404

  • Migration cleanup: URLs that no longer exist in the new site and have no semantic equivalent. Use 410. They are gone.
  • Deleted blog posts: old content you have intentionally retired. 410 if you are confident; 404 if there is any chance the post comes back under the same slug.
  • Decommissioned product pages: SKUs you no longer sell. 410 if the product is permanently retired; 404 if it might come back.
  • URL pattern retirement: e.g. retiring /forum/* because the forum was decommissioned. 410 the entire path.
  • Junk URLs from past hacks or CMS errors that have somehow ended up in the index. 410.

When 404 is the right call

  • Typos and never-existed URLs: 404 is the conventional response. 410 would be wrong because the URL never had content to begin with.
  • Content under review for republishing: if the URL might come back, 404 keeps the door open for Google to find it again.
  • Temporarily unavailable pages: 404 is wrong here too — use 503 with a Retry-After header. But absent that, 404 is the safer default for ambiguous cases.

How to set 410 on common platforms

Vercel (vercel.json)

Add a rewrite or status code rule. The cleanest pattern is to ship a /410 endpoint and rewrite retired URLs to it with statusCode 410. Note that vercel.json natively supports redirects, not status-code rewrites for arbitrary URLs; the pattern is to use a Next.js middleware that returns new Response(null, { status: 410 }) for any URL in your retirement list.

Netlify (netlify.toml or _redirects)

Use the _redirects syntax: /retired-url 410! — the trailing exclamation mark is the force-status flag. Or in netlify.toml, use a [[redirects]] block with status = 410.

Apache (.htaccess)

Redirect gone /retired-url — or use RewriteRule with the [G] flag: RewriteRule ^retired-url$ - [G,L].

Nginx

return 410; inside a location block matching the URL pattern. For bulk patterns: location ~ ^/old-pattern/ { return 410; }.

Why this matters at scale

A site with 10,000 retired URLs split 50/50 between 404 and 410 will see the 410 half drop from the Coverage report within two weeks, and the 404 half drift in the Coverage report for three to six months. The 404 set occupies Googlebot crawl budget that should be going to the URLs you want indexed. On bigger sites the budget impact is measurable: less budget for retirement means more for new content.

The migration pattern

Three steps for any migration cleanup. One: pull every URL from Search Console, Ahrefs, and the old sitemap. Two: categorise each URL into one of four buckets: (a) maps to a new URL → 301 redirect, (b) renamed to new slug → 301, (c) permanently retired → 410, (d) ambiguous status → 404. Three: ship the redirect/410/404 rules in vercel.json or .htaccess in the same deploy as the new content, plus a build-time SEO linter that fails the build if any pre-migration URL is missing from the rule set.

Worth knowing: the 451 code

451 Unavailable For Legal Reasons exists for content you have legally been required to remove. It is rare and specific — DMCA takedowns, GDPR right-to-erasure when the URL is retired entirely, court orders. Google interprets it similarly to 410 (drops from index) but the legal signal is what matters. Use it only when the legal context applies.

The summary

Use 410 for permanent retirement. Use 404 for genuinely missing or ambiguous content. The two-week vs six-month index-cleanup difference shows up on every migration over a few thousand URLs. The fix is one rule in vercel.json, netlify.toml, or .htaccess. Get it right at cutover and you save the post-launch cleanup that nobody budgets for.

Adjacent reading: 301 vs 302 vs 307 vs 308 is the related call for URLs that are not retired but moved. The same migration cleanup that needs 410 also needs the redirect-code decision; see the redirect strategy post for that frame.

< BACK TO BLOG