Fixing 404 Not Found Errors
A 404 Not Found response means the server has nothing at the address that was asked for. Visitors see a dead end, and search engines eventually drop the URL. Most sites collect 404s slowly as pages are renamed, products are withdrawn and links are mistyped, then collect hundreds at once after a redesign.
Not every 404 is a problem, and redirecting them all is a mistake. This guide shows you how to gather every missing URL in one list, decide what each one deserves, make the change, and stop the same kind of 404 coming back.
- What a 404 actually tells you
- Finding every 404, not just the ones you know about
- The fix, step by step
- Choosing between redirect, restore and retire
- Writing redirects on common set-ups
- Soft 404s: the errors that hide behind a 200
- After a redesign, migration or platform change
- Stopping new 404s appearing
- Questions and answers
What a 404 actually tells you
A 404 is the server saying "I understood the request, but there is nothing here". It says nothing about why. The page might never have existed, might have been deleted last year, or might exist at a slightly different address because a slug was edited. The code is the same in every case, so the status alone cannot tell you what to do. What decides the fix is the history of the URL and who is still asking for it.
It helps to separate the three groups of people who hit a 404:
- Visitors following your own links. A menu item, a related-posts block or a link in an old article still points at a dead address. These are entirely under your control and are always worth fixing.
- Visitors and crawlers following other people's links. Another site, a newsletter, a forum post or a bookmark points at a URL you removed. You cannot edit the link, so the fix is on your side: a redirect, or bringing the page back.
- Bots guessing. Vulnerability scanners ask for
/wp-login.php,/.envor/phpmyadmin/on sites that have none of those. These 404s are correct and should be left alone.
404 compared with 410 and soft 404
| Response | What the server says | When it is the right answer |
|---|---|---|
| 404 Not Found | Nothing here, no comment on whether it will return | Typos, guessed URLs, pages removed without a planned replacement |
| 410 Gone | Removed on purpose and not coming back | Discontinued products, expired offers, spam pages you deleted |
| 301 to a new URL | This content now lives elsewhere | The same content, or a close equivalent, exists at a new address |
| Soft 404 (200 with "not found" content) | Success, while showing an error or an empty page | Never. It confuses crawlers and wastes crawl requests |
Google has said for years that 404s for pages that really are gone do not harm the rest of a site. The cost is local: the visitor who hit the dead end, and any value the old URL had built up from links pointing at it.
Finding every 404, not just the ones you know about
No single source shows the whole picture. Each one sees a different slice, and you want them combined before you decide anything.
| Source | What it finds | What it misses |
|---|---|---|
| HTTP Error Audit | Every URL in your crawl scope that answers 404, with redirects followed to their final status | URLs outside the crawl scope, such as old addresses nothing links to any more |
| Search Console, Pages report, "Not found (404)" | URLs Google has tried recently, including ones from external links and old sitemaps | URLs Google has not tried; the list can lag behind reality by days or weeks |
| Server access logs | Every real request, from people and bots, with the referrer where the browser sent one | Nothing, but the noise from scanners needs filtering out |
| Analytics | Page views of your 404 template, if it is tagged with a distinct page title | Visitors who block analytics; bots |
| Backlink tools | External links pointing at dead URLs | Links the tool has not discovered |
The access log is the most honest source. On a typical nginx or Apache log in the combined format, this pulls the most requested missing paths over the period the log covers:
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn | head -50
To see which pages and sites are sending people to those dead paths, include the referrer field:
awk -F'"' '$3 ~ / 404 / {print $2 " | " $4}' access.log | sort | uniq -c | sort -rn | head -50
Filter out obvious scanner paths before you count. Anything containing wp-admin on a
site that is not WordPress, .env, .git, xmlrpc.php or cgi-bin
is almost always automated probing and needs no redirect.
/offers?utm_source=mail
and /offers are one row.The fix, step by step
Step 1
Collect every missing URL in one sheet
Run the HTTP Error Audit across the whole site and export every URL that
returned 404. Add the "Not found (404)" list from the Search Console Pages report, the top missing paths
from at least a month of access logs, and any dead targets from your backlink tool. Strip tracking parameters,
lower-case where your server treats paths case-insensitively, and remove duplicates. Delete rows for
scanner probes such as /.env. What remains is the real list you will work through, usually
far shorter than the raw totals suggested.
Step 2
Record where each 404 is linked from
For every URL, note whether the link comes from your own site, from another site, from an
old sitemap or from nowhere you can trace. A site search of your content or database finds internal links,
the access log gives the Referer header for many external visits, and Search Console shows
referring sitemaps and pages on the URL inspection panel. Add the number of hits over the last month.
A URL with internal links and steady traffic is urgent; a URL with no referrer and three hits a month
is not.
Step 3
Decide redirect, restore or retire for each URL
Go down the sheet and write one decision per row. Redirect when the same content, or a page that answers the same need, exists at another address. Restore when the page was deleted by mistake, still gets links, and nothing else covers it. Retire when the content is truly gone: leave it as a 404 or send a 410. Do not pick the homepage or a broad category as a catch-all target, because search engines often treat that as a soft 404 anyway.
Step 4
Write the 301 redirects as one-hop rules
Add each redirect as a permanent 301 from the old path straight to the final live URL, including the correct protocol, host and trailing slash. Check that the target returns 200 before you add the rule, so that you do not create a chain or send people to another 404. Use one exact rule per URL where there are only a few, and a pattern rule only when a whole folder moved in a predictable way. Keep all redirects in one place, not split between plugin, server and CDN.
Step 5
Correct your own links at the source
A redirect rescues visitors, but your own pages should link directly to the live address. Find every page that links to a dead or redirected URL, using your CMS search, a database query or a link crawler, and change the link in the content, menu, footer, widget or template. Search for the old path in widgets and reusable blocks too, because links there repeat on every page. Regenerate the XML sitemap so it lists only live URLs that return 200, and resubmit it in Search Console.
Step 6
Make the 404 page return a real 404
Request a made-up address such as /this-should-not-exist-7391 with curl -I
and confirm the first line shows 404, not 200 or a redirect. Then make the page itself useful: say plainly
that the page is missing, offer a search box, link to the main sections and popular pages, and keep the
full site header and navigation. Do not redirect unknown URLs to the homepage automatically, and make
sure empty search results and deleted product pages also send a 404 or 410.
Step 7
Recrawl and confirm in Search Console
Run
the HTTP Error Audit again and check that no URL in scope still answers 404 unless you
chose to retire it, and that every new redirect lands on a 200 in one hop. Test a sample of the redirected
URLs by hand with curl -sIL. In Search Console, use Validate Fix on the "Not found (404)"
group. Expect retired URLs to stay listed for a while; that is normal and needs no action. Diary a check
for a month later to catch 404s created since.
Choosing between redirect, restore and retire
The decision in step 3 is where most 404 clean-ups go wrong, usually by redirecting too much. A redirect is a promise that the new page gives the visitor what the old URL offered. When it does not, the visitor bounces and search engines tend to ignore the redirect.
| Situation | Best answer | Why |
|---|---|---|
| Post renamed, same content at a new slug | 301 to the new slug | Exact equivalent exists |
| Product replaced by a newer model | 301 to the new model | Visitors want the current version of the same thing |
| Product discontinued, nothing similar sold | 410, or 404 with helpful links | Any redirect would mislead |
| Three thin articles merged into one guide | 301 all three to the guide | The guide now covers each topic |
| Event page for a past event | Keep the page as an archive, or 410 | The next event is not the same thing |
Typo in an external link, such as /contcat | 301 to /contact | Cheap rescue for a clear intent |
| Deleted by accident, still linked from other sites | Restore the page at the original URL | Keeps the links working with no redirect at all |
| Guessed or scanner URL | Leave as 404 | Nothing to rescue |
When you are unsure, ask what someone arriving from the old link was trying to do, and whether the target page lets them do it within a few seconds. If the honest answer is no, a clear 404 page with good navigation serves them better than a redirect to something unrelated.
Writing redirects on common set-ups
Put redirects as close to the front of the stack as your set-up allows, and keep them in one place so the next person can find them.
Apache (.htaccess or virtual host)
# One page moved
Redirect 301 /services/boiler-repair /services/boiler-servicing
# A whole folder renamed, keeping the rest of the path
RedirectMatch 301 ^/blog/news/(.*)$ /news/$1
# Retired on purpose
Redirect gone /offers/summer-2024
nginx (server block)
location = /services/boiler-repair { return 301 /services/boiler-servicing; }
location ^~ /blog/news/ { rewrite ^/blog/news/(.*)$ /news/$1 permanent; }
location = /offers/summer-2024 { return 410; }
For long lists in nginx, a map block keeps hundreds of one-to-one redirects readable and fast:
map $uri $moved_to {
default "";
/old-range/oak-table /furniture/oak-dining-table;
/old-range/oak-chairs /furniture/oak-dining-chairs;
}
server {
if ($moved_to) { return 301 $moved_to; }
}
WordPress, Shopify and hosted builders
In WordPress, a redirect manager plugin or your SEO plugin's redirect feature stores rules in the database and can log 404s as they happen. WordPress also guesses at close matches for some missing URLs, which can hide typos; the WordPress HTTP errors guide covers the platform's own 404 quirks, such as rewrite rules that need flushing. Shopify has URL redirects under Online Store, Navigation (the exact menu location has moved over the years), and can create a redirect automatically when you change a product handle if you leave that option ticked. Squarespace, Wix and similar builders each have a redirects panel in site settings.
For detailed advice on avoiding multi-hop redirects once the rules pile up, see fixing redirect chains.
Soft 404s: the errors that hide behind a 200
A soft 404 is a page that looks like an error or an empty shell to a reader but returns 200 OK. Search Console reports these separately under "Soft 404", and the HTTP Error Audit will not flag them as 404s because the code is a success. They are common, and they usually come from templates rather than individual pages.
- Custom error templates that forget the status. A CMS or framework renders a friendly
"page not found" design but never sets the code. Check with
curl -Ion a made-up path. - Empty listings. Category, tag or search pages with no results, or filter combinations that match nothing.
- Out-of-stock or withdrawn products reduced to a title and a "no longer available" line.
- Single-page applications that serve the same HTML shell with 200 for every path and draw "not found" in JavaScript. The server or framework has to return 404 for unknown routes; the Node.js and Next.js guide shows how.
- Redirects to irrelevant pages, such as every old product sent to the homepage, which search engines may class as soft 404s.
The fix is to decide which it really is. If the page has no useful content, return 404 or 410. If it should exist, give it real content: an empty category could explain what will appear there and link to similar ranges, and a withdrawn product could name its replacement and link to it.
After a redesign, migration or platform change
The biggest wave of 404s nearly always follows a change of URL structure: a new CMS, a move from /index.php?id=12
style addresses to readable slugs, dropping date folders from blog URLs, or merging two sites. The clean-up
is much easier if you plan it before launch rather than after.
- Before the old site is switched off, export every URL it served, from the old sitemap, a full crawl and the last few months of access logs.
- Map each old URL to its new home in a two-column sheet. Mark anything with no equivalent as "retire".
- Load the map into your redirect rules and test the whole list against a staging copy before launch.
- After launch, recrawl the old list and check every row lands on a 200 in one hop.
- Watch Search Console and the access log closely for the next few weeks and add rules for anything you missed.
Keep migration redirects in place for a long time. Google has suggested at least a year, and there is little reason to remove a working redirect while old links still point at it. The migration redirects guide goes deeper into building and testing the map.
Stopping new 404s appearing
- Treat slugs as permanent. Once a page is published and shared, change its title freely but leave its URL alone unless there is a strong reason.
- Redirect at the moment of change. Make adding a redirect part of renaming or deleting any page, not a clean-up job months later. Many CMSs can prompt for this.
- Link from templates with generated URLs, not typed-in addresses, so a slug change updates menus and related links automatically.
- Keep the sitemap generated, never hand-edited, so it cannot list deleted pages.
- Log 404s continuously. A weekly look at the top missing paths in the access log, or a 404 log in your CMS, catches new problems while they are small.
- Run the audit on a schedule and after every release that touches navigation, templates or URL rules.
1. Should I redirect all 404 errors to my homepage?
No. Sending every missing URL to the homepage leaves visitors confused, because they asked for something specific and landed somewhere general. Search engines often treat those redirects as soft 404s, so any value the old URL had is not passed on anyway. It also hides new problems, because your audits and Search Console stop reporting them. Redirect only where there is a genuinely equivalent page, and let the rest return a clear 404 or 410 with helpful navigation.
2. How many 404 errors is too many?
There is no safe or unsafe number. A large site can have thousands of 404s from scanners, typos in other people's links and long-deleted pages, and that is perfectly normal. What matters is which URLs they are. Any 404 linked from your own pages, listed in your sitemap, or still receiving meaningful traffic or external links is worth fixing. The rest can be left. Judge the list by those questions rather than by the total count in a report.
3. Why does Search Console still show 404s I have already fixed?
Search Console reports what Googlebot saw the last time it requested each URL, and Google does not recheck every address immediately. Retired URLs you deliberately left as 404 also stay listed for a long time, because Google keeps trying known URLs now and then in case they return. Use the URL inspection tool on a sample to confirm the live response, and use Validate Fix for the group. If the live test shows the right code, the report will catch up in time.
4. Is a 410 better than a 404 for removed pages?
A 410 states that removal was deliberate, which is clearer to anyone reading the response. Google treats both codes very similarly, and a 410 may lead to a URL being dropped slightly sooner, but it is not a dramatic difference. Use 410 for content you have intentionally removed with no replacement, such as discontinued products or deleted spam, and leave ordinary unknown or mistyped URLs as 404. Either is far better than a soft 404 or a misleading redirect.
5. How long should I keep redirects for moved pages?
For as long as anything still links to or requests the old address. Google has indicated that a year is a sensible minimum for redirects after a site move, but there is rarely a reason to remove a working redirect after that. Old links in emails, printed material, bookmarks and other websites keep sending people for years. Check the access log for hits on the old path; if requests have genuinely stopped and nothing links to it, removing the rule is harmless.
6. Why do I see 404s for pages that never existed on my site?
Most of these come from automated scanners probing for common software weaknesses, such as login pages, configuration files and admin panels for platforms you may not even use. Some come from other sites linking to you with a typo, or from tools that build URLs incorrectly by joining relative paths. Scanner 404s need no action. Check the referrer on the rest, and add a redirect only where a real link with an obvious intended target is sending people to the wrong address.
7. Do 404 errors waste crawl budget?
Crawl budget is only a concern for very large sites, typically with hundreds of thousands of URLs or more. For most small business sites Google crawls everything it needs regardless of a few hundred 404s. What does waste crawling on any site is your own pages linking to dead or redirected URLs, and sitemaps listing pages that no longer exist, because crawlers keep following them. Fix those at the source and the remaining 404s are simply requests that correctly find nothing.
The HTTP Error Audit requests every URL in your crawl scope, records the status code and response time, follows redirects and flags anything outside the 200 range, so you can see every missing page before visitors and search engines do.
Open HTTP Error Fixes โ