A broken internal link is a link from one page on a site to another URL on the same site that doesn’t return a usable page, typically a 404 or 410, sometimes a 5xx. They waste crawl requests, leak link signals into dead ends and frustrate users. They’re also the finding clients understand fastest, which makes it worth getting right.
Where broken internal links come from
Knowing the cause tells you where to fix them:
- Deleted or renamed pages without a redirect (the most common cause after a redesign or CMS migration).
- Hard-coded links in templates: header, footer, sidebar or “related posts” widgets pointing to an old URL. These appear on every page that uses the template.
- Typos and relative-path mistakes, e.g.
href="services/roofing"on/blog/post/resolving to/blog/post/services/roofing. - Environment leftovers: links to staging hosts or development paths copied into content.
- Case and trailing-slash mismatches on servers that treat
/Aboutand/about, or/pageand/page/, as different URLs.
Step 1: Crawl the site from the homepage
A crawl that follows links is the only reliable way to find internal links, because it records source → destination pairs. Search Console tells you which URLs Google found returning 404, but not reliably which of your pages link to them today.
Configure the crawl so the result is trustworthy:
- Respect robots.txt and record which URLs were blocked. A blocked URL isn’t broken; it’s unknown.
- Use a delay between requests. A burst of 503s or 429s from a rate limiter will look like broken pages.
- Note the page limit. If the crawl stops early, you’ll miss broken links on pages it never reached.
- If the site builds navigation with JavaScript, use a rendering crawler or check templates manually. An HTML-only crawler won’t see those links (Google: crawlable links).
Step 2: Separate “broken” from “unconfirmed”
Not every failed request is a broken link:
| Result | What it means | Treat it as |
|---|---|---|
| 404 / 410 | The server says the page doesn’t exist | Broken (confirmed) |
| 5xx | Server error at the time of the request | Broken, but re-test: it may be intermittent |
| Timeout / connection reset / DNS failure | No HTTP answer at all | Unconfirmed: re-test before reporting |
| 401 / 403 | Access refused, often a login area or a firewall | Investigate; often intentional |
| 429 | Rate-limited | Your crawl was too fast; not a site defect |
Reporting a timeout as a 404 is the quickest way to lose a developer’s trust in your audit.
Step 3: Group by destination, not by occurrence
Raw crawl exports list every occurrence: page A links to missing URL X, page B links to X, and so on. Use three separate counts:
- Unique broken destinations: distinct broken URLs. This is the number of things to fix.
- Pages with broken links: distinct source pages affected.
- Occurrences: one page linking to one broken URL. A link repeated on the same page counts once.
A site with 1,200 occurrences may have only four unique broken destinations, three of them in the footer. Reporting “1,200 broken links” is technically true and practically misleading.
Step 4: Prioritise
Sort broken destinations by:
- Number of linking pages. High counts almost always mean a template link: one fix, many pages cleaned.
- Proximity to the homepage. A broken link on the start page or in the main navigation affects every visitor’s first click and the crawler’s main discovery path.
- Click depth of the linking pages. Links on shallow pages are seen more often by users and crawlers.
- Whether the destination had value. If the missing URL has external backlinks or historic traffic (check Search Console and your backlink data), a redirect matters more.
Step 5: Choose the right fix
- Update the link to the correct live URL. This is the best fix for internal links: no redirect hop at all.
- Add a 301 redirect from the missing URL to its closest equivalent when external links or bookmarks point to it as well. Then still update your internal links.
- Remove the link if there is no equivalent content.
- Don’t redirect everything to the homepage. Google may treat irrelevant redirects as soft 404s.
Fix template links first, then content links.
Step 6: Don’t forget links through redirects
Internal links that pass through a redirect (A → B) work, but every one costs an extra request and keeps old URLs alive in your templates. List each source → target pair once; most are fixed in a menu or a component. Chains (A → B → C) deserve priority.
Step 7: Verify, then monitor
Re-crawl with the same settings after the fixes. Compare the two audits: a broken link is only resolved if the newer crawl re-checked the same source page and the link is gone or now works. If the newer crawl didn’t reach the source page, the issue is not re-checked, not fixed. Comparing SEO audits over time explains this distinction.
Broken links come back with every content change, so a scheduled audit catches regressions before a client does.
Quick checklist
- Crawl from the homepage with robots.txt respected and a polite delay
- Record the page limit and whether the crawl completed
- Separate 404/410 from timeouts, 401/403 and 429
- Group by unique destination; report three separate counts
- Prioritise by linking pages, homepage proximity and destination value
- Fix templates first; update links rather than relying on redirects
- Re-crawl with identical settings and compare