# How to check for 404 errors

**Short answer:** "check 404 errors" covers two questions. Which links on your pages point to a page that returns 404? A link checker answers that: a crawler for a whole site, or a browser extension for one page. Which of your own URLs return 404 to visitors and Google? Search Console, your server logs or a 404 log answer that.

We make one of these tools: [LinkScan – Broken Link Checker](/), an extension for Chrome. It answers the first question for the page you have open. It does not answer the second: it checks the links on a page, not the URLs that visitors or Google request from your site.

## What a 404 is

A 404 means the server found nothing at that URL. It does not say whether the page is gone for good, and a server may also answer 404 to hide a page that exists ([RFC 9110, § 15.5.5](https://www.rfc-editor.org/rfc/rfc9110#section-15.5.5), the HTTP standard). A 410 says the page is gone, likely for good ([§ 15.5.11](https://www.rfc-editor.org/rfc/rfc9110#section-15.5.11)).

Each "Try it" link opens a live case on [a test page full of known broken links](/example/).

| Result | What it means | In LinkScan | Try it |
|---|---|---|---|
| 404 Not Found | Nothing at this URL | **Broken**, "Page not found — this link leads nowhere." | [404](/example/#S9-404) |
| 410 Gone | Removed, likely for good | **Broken**, "Page not found" | [410](/example/#S9-410) |
| A redirect that ends in a 404 | The old URL forwards to a missing page | **Broken**, with the final 404 | [Redirect to 404](/example/#S13-redirect-to-404) |
| Soft 404 | A "not found" page sent with status 200, or a redirect to an unrelated page | Direct 200 error body: **OK**. Deep-path redirect home: **Redirect**, even with one hop | [Soft 404](/example/#R-soft-404) |
| 429, 503, or no answer in 10 seconds | "Not now", not "gone" | **Unknown**, never Broken | [429](/example/#S9-429) |

## 404 check on a page

With LinkScan:

1. **Open the page.** To check one section only, such as an article body, select the text that contains its links.
2. **Click the LinkScan icon.** Chrome's side panel opens. Click **Scan All Links**, or **Check Selected Links** when text is selected.
3. **Read the Broken group first.** A 404 or 410 row says "Page not found — this link leads nowhere." and suggests "Update or remove this link". A URL repeated on the page is one row, with its count.
4. **Click the Locate on page button.** The page scrolls to the link, so you know what to edit.
5. **Fix the link, then [check again](#export-and-check-again).**

Results come in four groups: **Broken**, **Redirect & Accessibility**, **Unknown** and **OK**. Links to your site and to other sites are checked alike; email, phone and JavaScript links are skipped; dead same-page `#` targets are checked in the DOM, without a request. Each link is requested without your cookies, as a logged-out visitor, at most two requests a second. After a whole-page scan, the LinkScan icon in the toolbar shows the page's issue count, red when any link is broken.

On a help centre or knowledge base, scan each article.

[Add to Chrome — free](https://chromewebstore.google.com/detail/broken-links-scanner/jcfgjohakfnckffjncgfmoldibhbmofm). Find the 404 links on the page you have open, in your browser. No account.

## 404 broken link checker for a site

LinkScan checks one page at a time. It is not a crawler. To check a whole site, use a crawler: it follows links from page to page and lists each broken link with the page that contains it. The guide on how to [check a whole website for broken links](/guides/find-broken-links-on-a-website/) compares the kinds of tools.

A crawl finds two kinds of 404 links. For an internal one, on your own site, fix the link, restore the page or redirect the old URL. For an external one, you can only update or remove the link.

## Your own 404 pages

The second question is about URLs on your site that answer 404 when someone requests them: an old bookmark, a mistyped link on another site, a page you deleted. A link checker does not see these requests.

- **Search Console.** The [Page indexing report](https://support.google.com/webmasters/answer/7440203) lists URLs Google tried to crawl under "Not found (404)", and the soft 404s it found. It needs a verified site and shows only what Google requested.
- **Server logs.** Your host's access logs record every request that got a 404. Filter for status 404 and sort by count.
- **Analytics or a 404 log.** Count views of your 404 page, or use a plugin that records each 404 hit. This shows what real visitors hit. After a relaunch, see [which old links visitors still follow](#old-links-visitors-still-follow-after-a-relaunch).

Google says 404s generally do not affect search performance when the URLs should not exist ([Search Console Help](https://support.google.com/webmasters/answer/2445990)). Fix the URLs that should work: a moved page, a typo, an old URL other sites link to. The rest can stay 404.

Make the page those visitors see useful: explain that the address is missing and offer home, navigation and a way to report the link.

## 404 scanner or 404 log?

Tools for 404s do three different jobs:

| Tool | What it finds |
|---|---|
| 404 scanner (a link checker) | Links on your pages that point to a 404 |
| 404 log or monitor | Requests to your site that got a 404, over time |
| Bulk URL checker | The status of each URL in a list you paste in |

LinkScan is a scanner for one page. It does not log visits, send alerts or take a list of URLs.

## Is it really a 404?

A server can answer a checker differently from your browser. These cases matter for 404s. For every kind of result, see [what each result means](/guides/find-broken-links-on-a-website/#read-the-result-before-you-fix-it).

| Case | What happens | Result in LinkScan v2.1 | What to do | Try it |
|---|---|---|---|---|
| HEAD gets 404, the page gets 200 | A checker that asks only for headers (HEAD) gets a false 404 | **OK**: a HEAD 404 is retried with a full GET | Nothing | [HEAD 404](/example/#S9-head-404) |
| Bot protection | 403 to the checker, 200 when you click | **Unknown** if the response carries a recognised challenge signal; plain 403 stays **Broken**, "Access denied" | Open it in a private window. If it opens, keep the link and mark it as known. | [Bot wall](/example/#R-bot-wall) |
| Login needed | A redirect to a login page | **Unknown**, "Needs login" for a recognised login redirect | If that is not what you meant to link, treat it as broken. | [Login gate](/example/#R-login-gate) |
| A private page | The server answers 404 rather than reveal the page | **Broken**, 404 | If it opens when you sign in, it works for signed-in readers only. | – |
| Soft 404 | A "not found" page with status 200, or a redirect to the home page | Direct 200 error body: **OK**. Deep-path redirect home: **Redirect**, even with one hop | Open links whose final URL looks wrong. | [Soft 404](/example/#R-soft-404), [by redirect](/example/#R-soft-404-redirect) |

The HTTP standard says a server should answer HEAD with the same headers as GET ([RFC 9110, § 9.3.2](https://www.rfc-editor.org/rfc/rfc9110#section-9.3.2)). It says what servers should do, not what every server does.

The soft 404 is the reverse: a 404 the checker misses.

## After a move or redesign

This is not a migration checklist. A move needs a redirect map and a crawl before and after; LinkScan helps you check key pages by hand.

**What breaks:**

- Internal links still point at old URLs, which now return 404 or redirect.
- Redirects from earlier moves stack up: old → interim → new is a redirect chain.
- Old URLs redirect to the home page: a soft 404.
- A switch to https or to another `www` form adds a hop to every old link.
- Case or trailing-slash changes. On this site's server, `/Contact/` and `/contact` redirect to `/contact/` ([uppercase](/example/#S16-uppercase), [no slash](/example/#S16-no-slash)); a server that treats them as different URLs answers 404.

**Across the site,** map old URLs to new ones and crawl before and after (see [scan a whole site](/guides/find-broken-links-on-a-website/#scan-a-whole-site-for-broken-links)). In the weeks after, Search Console's Page indexing report shows old URLs Google still requests under "Not found (404)".

**Where LinkScan fits:** check the home page, the menu and footer, your top landing pages, and one page of each template. A link with two or more redirects shows as **Redirect chain**, "Chain: N hops → final URL", with the fix "Update href to final destination URL" ([chain of 2](/example/#S13-chain-2)). A redirect that ends in a 404 shows as **Broken** ([redirect to 404](/example/#S13-redirect-to-404)), and a loop as **Broken**, "Redirect loop", after 10 hops. The Lab's [loop](/example/#S13-loop) and [http + www](/example/#S13-http-www) cases show what the v2.1 specification predicts. Check several pages, then open **History** for their results or **Domains** to see them grouped by host. Export each page separately.

**For Google,** a 301 or 308 is a strong signal that Google should process the new URL, a 302 or 307 a weak one, and URLs that return 4xx are not indexed ([Google's status code documentation](https://developers.google.com/crawling/docs/troubleshooting/http-status-codes), for Google's crawlers). Use a permanent redirect for a page that moved for good.

If an AI agent (Claude Code, Cursor, ChatGPT and so on) works on your site's code or CMS, this prompt has it point internal links at their final URLs; for a full redirect map, use the [redirect-map prompt](#read-the-report).

```prompt
Site: [YOUR SITE URL], which changed [WHAT MOVED: domain / https / www / URL structure]. Code or CMS: [REPO PATH / CMS]. Old → new: [REDIRECT MAP OR PATTERNS].

Goal: internal links that point straight at current URLs, with no 404s and no redirect hops.

- Look for old paths, the old host, http://, the other www form, and case or trailing-slash differences.
- Trace each target with `curl -I -L`. Flag chains (two or more redirects), redirects ending in 404, loops, and redirects to the home page (soft 404s).
- Change links to the final URL. Propose a 301 or 308 only for our own old URLs that people elsewhere still follow.

Inspect first, propose a table, and wait for my approval before editing, publishing or deploying. Report what changed.
```

## Old links visitors still follow after a relaunch

After a new website launches, people keep arriving with old addresses nobody can update. Your server log and Search Console say which URLs return 404. They don't say whether the visitor came from your own page, another website, or nowhere a browser can see. This section is for sites that have just moved. The 404 page should explain what happened and offer useful next steps.

### Where old links live

- **Print:** QR codes on posters and flyers, magazine ads and other print.
- **Email:** forwarded email chains and old newsletters.
- **Other sites** that link to old addresses.
- **Your own pages:** internal links nobody updated, the one place you control directly.

A printed flyer or a sent email can't be changed. The fix is on your side: a redirect, and a 404 page written for visitors with old links.

### Before launch

1. **Map and redirect.** Google's site-move guide says to "decide where each one should redirect to" and to use "server side permanent redirects", a 301 or 308 ([Site moves with URL changes](https://developers.google.com/search/docs/crawling-indexing/site-move-with-url-changes), updated 2026-08-20). For chains and case or slash changes, see [after a move or redesign](#after-a-move-or-redesign); the [redirect-map prompt](#read-the-report) works from your old sitemap too.
2. **Print and QR: point at a redirect you control.** Print a short address such as `example.org/apply`, or a QR code that opens one, and make it a redirect on your domain to the current page. After the next redesign you change one redirect instead of reprinting. This is our advice, not a Google rule.
3. **Campaign parameters.** Add `utm_source`, `utm_medium` and `utm_campaign` to the redirect's target. Google says you "should always use" all three ([URL builders](https://support.google.com/analytics/answer/10917952)). A referrer can't say "poster" or "newsletter"; these can, and GA4 reports them as Session source/medium.

### Track 404s in GA4

If your site runs GA4, five steps in Google Tag Manager report which missing URL was requested and where the visitor came from. All event, parameter and variable names below are examples. With gtag.js and no Tag Manager, one `gtag('event', …)` call sends the same event ([Set up events](https://developers.google.com/analytics/devguides/collection/ga4/events), updated 2026-06-09), as [our own 404 page](#live-example) does. The Google help pages cited here show no date; we read them on 2026-10-10.

If an AI agent (Claude Code, Cursor, ChatGPT and so on) works on your site's code, this prompt has it add the event.

```prompt
Site: [YOUR SITE URL], code in [REPO PATH]. GA4 loads through [TAG MANAGER / GTAG.JS].

Goal: on the 404 page, send the GA4 event `page_not_found` with:
- `missing_url`: the path only, no query string;
- `referrer`: the referrer without its `?` or `#` part, max 100 characters;
- `referrer_type`: `none` if no referrer, `internal` if its hostname is one of [OUR HOSTNAMES], else `external`.

No personal data. Keep the 404 status. Check that the tag loads on 404 responses. With Tag Manager, push the event to the data layer from the 404 template; don't detect the 404 by page title. Don't change Tag Manager or GA4 settings yourself.

Inspect first, propose, and wait for my approval before editing, publishing or deploying. Verify: a typed URL gives `none`, a link from our page `internal`, another site `external`. Report what changed and what I must set up: register the three parameters as GA4 custom dimensions (Scope: Event) the day it goes live.
```

1. **Mark the 404 page.** Have the 404 template push an event to the data layer, above the Tag Manager container code ([The data layer](https://developers.google.com/tag-platform/tag-manager/datalayer), updated 2026-07-22):

   ```html
   <script>
     window.dataLayer = window.dataLayer || [];
     window.dataLayer.push({ event: 'page_not_found' });
   </script>
   ```

   A **Custom Event** trigger with **Event name** `page_not_found` catches it ([Custom event trigger](https://support.google.com/tagmanager/answer/7679219)). Without a push, filter the trigger to "Some" events on something only the 404 template sets ([About triggers](https://support.google.com/tagmanager/answer/7679316)). Don't match the page title: an editor, a translation or a CMS suffix changes it, and the trigger silently stops firing or fires on the wrong page.

2. **Variables.** Check that the built-in variables **Page Path**, **Page Hostname** and **Referrer** are enabled ([Built-in variables](https://support.google.com/tagmanager/answer/7182738)). Then add a **Custom JavaScript** variable, "an anonymous function that returns a value" ([User-defined variable types](https://support.google.com/tagmanager/answer/7683362)), named for example `Referrer type`:

   ```js
   function () {
     var ref = {{Referrer}};
     if (!ref) { return 'none'; }
     var host = ref.split('/')[2];
     return host === {{Page Hostname}} ? 'internal' : 'external';
   }
   ```

   Tag Manager doesn't strip the query string from the Referrer, so add a second Custom JavaScript variable, for example `Clean referrer`, that removes it and cuts the result to 100 characters:

   ```js
   function () { var r = {{Referrer}}; return r ? r.split(/[?#]/)[0].slice(0, 100) : ''; }
   ```

   A site served on both `www.example.org` and `example.org` adds the other hostname to the comparison. By default a browser sends only another site's origin (`https://news.example.com/`), the full URL from your own pages, and nothing from HTTPS to HTTP ([MDN, Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy), modified 2026-09-04). QR scanners, typed addresses, bookmarks and most email apps send no referrer, so the `none` bucket needs the [campaign parameters](#before-launch).

3. **A GA4 Event tag.** Tag type **Google Analytics: GA4 Event**, your **Measurement ID**, **Event Name** `page_not_found`, fired by the step 1 trigger ([Set up Google Analytics events in Tag Manager](https://support.google.com/tagmanager/answer/13034206)). A custom event name must start with a letter ([Custom events](https://support.google.com/analytics/answer/12229021)) and stay under 40 characters ([Event collection limits](https://support.google.com/analytics/answer/9267744)). Add parameters, for example:

   | Parameter (example) | Value |
   |---|---|
   | `missing_url` | `{{Page Path}}`, the path without the query string |
   | `referrer` | `{{Clean referrer}}`, the referrer without its query string |
   | `referrer_type` | `{{Referrer type}}`: `internal`, `external` or `none` |

   Every web event already carries `page_location` and `page_referrer` ([Automatically collected events](https://support.google.com/analytics/answer/9234069)). What GA4 lacks is `referrer_type`, so send `missing_url` and `referrer` only as cleaned copies. "Both the URL path and parameters must be free of PII" ([Best practices to avoid sending PII](https://support.google.com/analytics/answer/6366371)): old email links put addresses in query strings. GA4's [data redaction](https://support.google.com/analytics/answer/13544947) looks for email addresses in all event parameters, but removes the query parameters you list only from `page_location`, `page_referrer` and a few other built-in fields, not from your own parameters.

4. **Register the custom dimensions.** In GA4: Admin › Custom definitions › Custom dimensions › **Create custom dimensions**: **Dimension name**, **Scope** Event, **Event parameter**, then **Save** ([Create event-scoped custom dimensions](https://support.google.com/analytics/answer/14239696)). Reports can use them "after 24-48 hours", so register them the day the tag goes live, and don't count on earlier data.

5. **Build the report.** In Explore, start a **Free form** exploration ([Free form exploration](https://support.google.com/analytics/answer/9327972)). **Rows:** `missing_url`, `referrer_type`, `referrer`. **Values:** Event count. **Filter:** Event name `page_not_found` ([dimension and metric names](https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema)).

### Read the report

- **Internal first.** `internal` means a link on your own page is broken, and `referrer` names that page. Open it, click **Scan All Links** in LinkScan and fix the **Broken** link ([404 check on a page](#404-check-on-a-page)). Before a print run or an email send, check the pages it links to the same way, or select a newsletter's links and click **Check Selected Links**.
- **Then external and none, busiest first.** Redirect each busy missing URL to its current page and leave the long tail as 404 ([fix a 404](#fix-a-404)). Split the `none` bucket by Session source/medium.
- **Cross-check** Search Console's "Not found (404)" list and your server log ([your own 404 pages](#your-own-404-pages)).
- **Two views:** the last 7 days after a release or a print run; the last 28 days for the redirect list.
- **Where LinkScan stops.** It checks the links on a page you open. It doesn't see QR scans, visits or requests to your server, and it has no analytics.

To turn the busiest missing URLs, an old sitemap or a list of printed addresses into redirects, give your AI agent this prompt.

```prompt
Site: [YOUR SITE URL] on [PLATFORM], config in [REPO PATH]. Old URLs: [LIST FROM GA4, SERVER LOG, SEARCH CONSOLE, OLD SITEMAP OR PRINT].

Goal: a redirect map from old URLs people still follow to the best current page, busiest first.

- 301 (or 308) to a page with the same purpose, at its final URL, in one hop.
- Nothing replaces it: leave a 404, or 410 if removed on purpose. Never send everything to the home page.
- Printed and QR addresses stay as permanent redirects.
- Check each target with `curl -I` and in a browser: a 403 to curl may be a bot challenge.

Inspect first, propose a table with a confidence and reason per row, and wait for my approval before editing, publishing or deploying. Report rows done, skipped and failed.
```

### Live example

linkscan.app runs GA4 with gtag.js, not Tag Manager, so its 404 page template sends the event directly:

```js
var ref = document.referrer || '';
var refType = 'none';
if (ref) {
  var host = '';
  try { host = new URL(ref).hostname; } catch (e) {}
  refType = (host === 'linkscan.app' || host === 'www.linkscan.app' || host === location.hostname)
    ? 'internal' : 'external';
}
if (typeof gtag === 'function') {
  gtag('event', 'page_not_found', {
    missing_url: location.pathname.slice(0, 100),
    referrer: ref ? ref.split(/[?#]/)[0].slice(0, 100) : '',
    referrer_type: refType
  });
}
```

The path is cut to 100 characters, the referrer loses its query string and fragment, and nothing else is sent. The script runs only on linkscan.app. Our host's injected tag is missing from 404 responses, so the page loads gtag.js itself when no gtag is there yet. [Open a made-up linkscan.app address](/this-page-does-not-exist-404-guide/) to see the page.

## Fix a 404

Decide link by link:

- **The page moved.** Update the link. If the old URL is yours, add a 301 redirect too, so bookmarks and outside links still work.
- **The page is gone, but a replacement does the same job.** Link to it, and redirect your old URL there.
- **The page is gone, and nothing replaces it.** Remove the link. Let the old URL return 404, or 410 if you removed it on purpose.
- **A redirect chain.** Link straight to the final URL the row shows.
- **Unknown or Access denied.** Open the link first, and check again later.

Do not send every missing URL to your home page. Visitors who wanted a specific page must search again, and the redirect is a soft 404. For a removed page, Google prefers a real 404 or 410 to a soft 404 ([Google Search Central blog, 2011](https://developers.google.com/search/blog/2011/05/do-404s-hurt-my-site)).

## Export and check again

After a scan, click **Export**. **Copy as table** copies rows that paste into a spreadsheet, with the columns Severity, Link text, Location, Page, URL, Status, Issue and Fix. **Copy to Clipboard** copies the findings as plain text. Export copies the issues the current filter shows. OK rows, including ordinary single redirects without another finding, and links you marked as known are left out; select **All** first to copy every issue.

The **History** tab lists scanned pages and their earlier scans; **Domains** groups pages by host. The **Links** tab lists checked URLs across pages: filter it to **Broken**, or limit it to links found on a page or domain. Export belongs to one page. Pages and links expire after 30 days under the retention rules.

After you fix a link, check it again. A normal **Re-scan** reuses the results for links checked recently (48 hours by default; change it under **Staleness threshold** in Settings), without requesting them again. For a fresh check, click the refresh button (**Re-check**) on the link's row, or open the page in a new tab, open the side panel there and click **Rescan** on the "Results for [site] from [age]" notice.

To have an AI agent fix the exported links in your code or CMS, use [the prompt in the broken links guide](/guides/find-broken-links-on-a-website/#fix-what-you-find-update-remove-or-redirect).

## FAQ

### What is the difference between a 404 and a soft 404?

A 404 is a status code that says nothing is there. A soft 404 is a "not found" page sent with status 200, or a redirect to an unrelated page. A direct 200 error page still shows as OK because the body is not read. LinkScan flags a deep-path redirect home as Redirect, and a redirect ending at a recognised not-found path as Broken. Search Console reports the soft 404s Google finds on your site. [Try the soft 404 case](/example/#R-soft-404).

### Do 404s hurt SEO?

Google says 404s on URLs that should not exist do not hurt the rest of your site ([Google Search Central blog](https://developers.google.com/search/blog/2011/05/do-404s-hurt-my-site)). A URL that returns a 4xx code drops out of Google's index. The cost is to visitors, and to internal links that point at pages you want found.

### Is there a free 404 checker that runs without an online form?

Yes. LinkScan runs in your browser after a free install, with no account.

[Add to Chrome — free](https://chromewebstore.google.com/detail/broken-links-scanner/jcfgjohakfnckffjncgfmoldibhbmofm). Find the 404 links on the page you have open, in your browser. No account.

---

Source: https://linkscan.app/guides/check-404-errors/ (2026-10-12)
