Skip to content
← Tilbage til Learn
Crawling & Indexing Kritisk

Broken internal links

Internal 404s leak link equity and burn crawl budget. Find them, decide redirect vs remove, fix at the template — not URL by URL.

Every internal link is a vote of confidence. A link to a 404 is a vote thrown in the trash — the receiving page doesn’t exist, the equity has nowhere to land, and Googlebot wasted a fetch confirming the page is dead.

A handful of broken links is normal. Thousands suggest a template, a stale CMS link picker, or a section migration nobody finished. Those are the patterns worth fixing.

How to detect it

The link is on the source page. The 404 is on the target. You need to join them.

# Crawl the site, list every link, check every target
# Replace with a real crawler (Screaming Frog, Sitebulb, the Serpwise Crawler).
# At minimum:
curl -s https://example.com/sitemap.xml \
  | grep -oE 'https://[^<]+' \
  | while read url; do
      status=$(curl -sI "$url" | head -1 | awk '{print $2}')
      echo "$status $url"
    done | grep -E '^4[0-9]{2}'

Real crawlers also follow the links on each page, not just the sitemap, which is what surfaces the source pages that link to the broken targets.

In Google Search Console: Pages → Not indexed → Not found (404) lists 404s Google saw. The “Referring pages” panel for each one shows where the broken link lives.

The fix — decide first

Two questions per broken target:

  1. Is there a replacement URL?
  2. Was the broken link the intent, or is the link just stale?

Decision tree:

  • Real replacement exists, intent matches → fix the link to point at the replacement, or 301 the dead URL to the replacement and leave the link.
  • No replacement, page is gone for good → remove the link entirely, return 410 on the dead URL.
  • Page genuinely should exist → restore the page.

Don’t blanket-redirect 404s to the homepage. That’s a soft 404 and signals nothing useful to Google.

Where the fix lives

<!-- before -->
<a href="/products/discontinued-widget">Our flagship widget</a>

<!-- after -->
<a href="/products/widget-2">Our flagship widget</a>

For template-level cleanup, find the template that emits the link and fix it once. URL-by-URL fixes don’t scale and the same link will reappear when the template re-renders.

WordPress

The most common source of broken internal links is the visual editor’s link picker, which stores href as a permalink at the time of insertion. If you change a slug, every linking post still points at the old permalink.

Fix:

  1. Install Broken Link Checker or run a crawler externally.
  2. Update the links at the source in bulk via WP-CLI search-replace:
wp search-replace 'href="/old-slug/"' 'href="/new-slug/"' wp_posts --dry-run

Drop --dry-run once you’ve confirmed.

Shopify

Two pitfalls: (1) deleted products leave hard-coded links in pages and blog posts, (2) collection slugs change and theme code references the old slug.

For (1), search the theme and pages for the dead URL using Shopify’s bulk editor or the GraphQL Admin API. For (2), grep the theme for hard-coded slugs and replace with {{ collection.url }}.

Static site generators (Astro / Hugo / Eleventy)

Run a link checker in CI:

# As a CI step
npx linkinator https://your-preview-url --recurse --skip "^(?!https?://your-preview-url)"

Fail the build on 404s. The cheapest fix is the one that never ships.

Next.js

Use the typed <Link> component and the typedRoutes config option in next.config.ts:

// next.config.ts
const config = {
  experimental: { typedRoutes: true },
};

With typed routes on, a link to a route that doesn’t exist is a TypeScript error. Most broken-link patterns become impossible to ship.

Pitfalls

Don’t fix 404s by 301’ing every dead URL to the homepage. Google flags large redirect-to-root patterns as soft 404. The dead URL still doesn’t count for anything, and now neither does the redirect.

Don’t only check links on pages in the sitemap. Footer links, nav links, and CMS components are often the source of the worst broken-link clusters. Crawl the whole site, not just the sitemap.

Don’t chase one-off broken links manually. If you find 30 broken links and 25 of them came from the same template, fix the template. The other 5 are the actual content cleanup.

Don’t ignore anchor text on broken links. A broken link with the anchor “buy our flagship widget” is leaking high-intent equity. Those are the ones worth fixing first.

Fix at the edge with Serpwise

When a template lives in a stack you don’t fully control — a CMS theme on a hosted vendor, a section behind a different team — fixing the source can take weeks.

Serpwise can rewrite href values at the edge based on URL patterns: “everywhere href="/products/discontinued-widget" appears, rewrite to href="/products/widget-2".” Or, “everywhere href="/blog/2019/*" returns 404, prepend a 301 at the edge.” The fix applies to every page, every visitor, every crawler — instantly. The long-term template fix lands on its normal schedule.

See pricing or run a free AI visibility audit.

Fra diagnose til live rettelse

Find issue'et. Få rettelsen live.

Brug Learn til at forstå problemet, og kør derefter Serpwise på dit eget site for at se, hvad der kan rettes og komme live.