Fixing Broken Internal Links at the Source
A broken internal link is a link on your own site that points at a page which no longer exists, never existed, or returns an error. Visitors hit a dead end, crawlers waste requests, and the page you meant to support receives nothing.
On most sites the report looks worse than it is: a few hundred broken links often trace back to a handful of causes, such as one wrong menu item, one hard-coded path in a template, or one page that was deleted while other pages still linked to it. This guide shows how to find those sources and fix each one once.
What counts as a broken internal link
Any link from one of your pages to another address on your own domain that does not end in a working page. That covers more than the familiar 404.
| What the link reaches | Status | Treat it as |
|---|---|---|
| A page that does not exist | 404 Not Found | Broken. Fix the link. |
| A page removed on purpose | 410 Gone | Broken from the visitor's side. Remove or repoint the link. |
| A page that errors | 500, 502, 503 | Check the page first; the link may be fine and the page faulty. |
| A redirect to another page | 301, 302, 307, 308 | Not broken, but an avoidable hop. Point the link at the final address. |
| A page that says "not found" but returns 200 | Soft 404 | Broken in practice. Fix the link, and fix the page's status code. |
A #section that no longer exists on the page | 200 | The page loads but the jump fails. Worth fixing on long guides. |
| A staging, development or old domain | Varies | Broken or leaking. Always replace with the live address. |
Links to other websites that no longer work are a separate subject, as are pages that other sites link to on your domain. This guide stays with links you publish between your own pages, and in particular with where in your site those links are generated.
Why one mistake becomes hundreds of broken links
The Internal Link Analysis finds broken links across your crawled pages and lists which pages contain them. When you first open that list, look at two numbers side by side: the number of broken link occurrences and the number of different broken destinations.
If there are 800 occurrences but only 6 destinations, you do not have 800 problems. You have 6, each repeated by something that appears on many pages. Typical multipliers are:
- The main menu or footer. One wrong item appears on every page of the site.
- A sidebar or widget. "Popular posts", "Our services" or a promo block that shows on every post.
- A theme template. A hard-coded link to
/contact-us/when the page is now/contact/, sitting in the header, the product template or the article footer. - A reusable content block. WordPress synced patterns, page builder global sections and Shopify theme sections store content once and show it in many places.
- Tag or category links. A deleted tag still referenced by old posts produces a broken link from every one of them.
Content links, the ones typed into individual paragraphs, usually produce long lists of different destinations with one or two occurrences each. They are slower to fix, so sort them by the importance of the page they sit on.
Common causes and where they live
| Cause | Tell-tale sign | Where to fix |
|---|---|---|
| Page renamed or slug edited | Old address still used in many links | Menus and content; add a redirect as a safety net |
| Page deleted without checking links | Destination returns 404 or 410 | Every page that linked to it |
| Relative path written wrongly | Link like services/repair without a leading
slash resolves differently on each page | Template or content: use root-relative /services/repair/ |
| Trailing slash or case mismatch | /About works locally but not on the server | Template; match the exact live address |
| Site moved from staging or an old domain | Links contain staging.example.co.uk
or http:// | Database search and replace |
| Framework base path or locale prefix | Links missing /en-gb/ or a subfolder | Router or link component configuration |
| Product or post unpublished | Related blocks or collections still link to it | Widget query or manual list |
| Typing error | One-off odd address | The single paragraph |
The relative path trap
A link written as href="pricing" means "pricing, relative to the current folder". On
/services/ it resolves to /services/pricing; on /blog/2026/winter-tips/
it resolves to /blog/2026/winter-tips/pricing. The same template therefore produces a different
broken address on every page. Use root-relative links that start with a slash, such as /pricing/,
in templates and menus.
Finding the exact element that creates a link
Knowing which page contains a broken link is only half the job. You need to know which part of the page wrote it. Open one of the source pages, then open developer tools and paste this into the Console, replacing the path with the broken destination:
const bad = '/old-services/boiler-repair/';
document.querySelectorAll('a[href]').forEach(a => {
if (a.pathname === bad) {
console.log(a.closest('header, nav, footer, aside, main, [class]'), a);
}
});
Each result shows the nearest landmark or classed element, which usually tells you straight away whether the link is in the header, a menu, a sidebar widget or the main content. Click the logged element to jump to it in the Elements panel.
Use your server logs too
Your access logs record the page the visitor came from in the referrer field. Filtering 404 responses where the referrer is your own domain gives you a live list of broken internal links that real people are clicking, which is a good way to prioritise. On a typical Linux server:
grep ' 404 ' access.log | grep 'example.co.uk' | awk '{print $7, $11}' | sort | uniq -c | sort -rn | head -30
Field positions vary with the log format, so adjust the column numbers if your output looks wrong.
Check the status yourself
Before editing, confirm what the destination really returns. curl -sI https://example.co.uk/old-services/boiler-repair/
shows the status line and any Location header. A page that loads in your browser may still
return a 404 status, and a page that looks missing may be a temporary server error.
Where to fix it on each platform
WordPress
- Menus: Appearance, Menus in classic themes, or the Navigation block in the Site Editor for block themes. Custom links store a typed address and do not update when a page slug changes, whereas menu items added as pages follow the page.
- Widgets and template parts: check sidebar widgets, footer template parts and synced patterns, which are stored once and shown everywhere.
- Content in bulk: WP-CLI can search and replace across the database, including serialised data, which a raw SQL replace would corrupt. Always run a dry run first and take a backup:
wp search-replace '/old-services/boiler-repair/' '/services/boiler-repair/' --dry-run
wp search-replace '/old-services/boiler-repair/' '/services/boiler-repair/'
- Page builders: global sections, headers and footers built in a page builder are edited in the builder's template library, not in Appearance, Menus.
Shopify
- Menus: edited in the admin under the online store's navigation settings. Menu items that point at a product, collection or page by selection follow that resource; typed web addresses do not.
- Theme settings: banners, slideshows and featured sections often store a link in the theme editor. A deleted collection leaves those links pointing nowhere.
- Descriptions: product, collection and blog content may contain typed links. The bulk editor or an export and re-import helps for large catalogues.
- URL redirects: Shopify lets you add redirects for changed handles, and offers to create one when you edit a handle. That keeps old links working, but you should still update the links themselves.
Static sites and frameworks
- Link components: in Next.js, Nuxt, Astro and similar frameworks, check the configured base path, trailing slash setting and locale prefix. A mismatch breaks every link the component writes.
- Hard-coded paths in layouts: search the source for the broken path with your editor or
grep -rn. - Case sensitivity: Linux servers treat
/About.htmland/about.htmlas different files, while your laptop may not. Keep filenames and links lower case.
The fix, step by step
Step 1
Group the broken links by destination
Export the broken links found by the Internal Link Analysis into a spreadsheet and sort by the broken destination address. Add a column counting how many source pages link to each destination. A destination linked from nearly every page almost certainly comes from a menu, header, footer or sidebar. A destination linked from one or two pages is probably a typing error or an old link in content. This grouping turns a long, alarming list into a short set of causes.
Step 2
Find the element that creates each link
For each destination, open one source page and locate the link in developer tools. Note whether it sits in the header, navigation, footer, a widget, a reusable block or the main content. Write that location into your spreadsheet. Check whether the link is typed as a relative path without a leading slash, contains an old domain, or differs from the live address only by a trailing slash or capital letter, because those point to a template fix rather than a content fix.
Step 3
Decide what the link should point to
For each broken destination, choose the correct replacement. If the page moved, use its new address. If it was merged into another page, point to the page that now covers the subject. If the content is gone for good and nothing replaces it, remove the link and adjust the sentence rather than sending people to a vaguely related page. Record the decision beside each row so anyone applying fixes later uses the same target consistently across the site.
Step 4
Fix menus, templates and widgets
Start with the links that repeat on many pages. Correct menu items, preferably by selecting the page itself rather than typing an address, so future slug changes follow automatically. Edit header, footer and sidebar templates, synced patterns and page builder global sections. Replace relative paths with root-relative ones starting with a slash. Clear any page or CDN cache after saving, then check two or three pages to confirm the corrected link appears everywhere the template is used.
Step 5
Fix links inside page content in bulk
For content links, use a bulk method where the same old address appears on many pages: wp search-replace
with a dry run on WordPress, a product export and re-import on Shopify, or a project-wide find and replace
on a static site. Take a backup first. For one-off errors, edit the individual page, starting with pages
that get the most visits. Check the anchor text still describes the new destination after you change
where the link points.
Step 6
Update links that pass through redirects
From the same analysis, list internal links whose destination returns a 301 or 302 redirect. These still work for visitors, but each one adds a hop, and a chain of several redirects slows things further. Replace each link with the final address the redirect lands on. Keep the redirects themselves in place for bookmarks and links from other websites, but make sure your own pages link directly to the live address.
Step 7
Add a check before anything is published
Put a guard in place so the list does not refill. Before deleting or renaming a page, look up which pages link to it and update them in the same session. On static or framework sites, run a link checker such as lychee, linkinator or htmltest against the built site in your deployment pipeline so a broken internal link fails the build. Then re-run the Internal Link Analysis to confirm the count has fallen.
Redirect or update the link?
A redirect and a corrected link solve different problems, and a tidy site usually needs both.
- Add a redirect when a page's address changes and people outside your control may still use the old one: bookmarks, emails you have already sent, printed material and links from other websites. The redirect catches them.
- Update your own links in every case. You control them, so there is no reason to make visitors and crawlers go through a hop. Internal links that point at redirects also make your link reports harder to read, because the destination shown is not the page that loads.
- Do not redirect everything to the homepage. When a page is gone and nothing replaces it, a redirect to the homepage confuses visitors and search engines tend to treat it as a soft 404 anyway. A proper 404 or 410 page with helpful navigation is the honest answer; then remove the internal links that pointed there.
If your redirects have built up into chains, where A redirects to B and B to C, the redirect chains guide covers flattening them. Fixing your internal links first reduces how often those chains are used at all.
/blog everywhere, can alter far more than the
links you intended.Stopping broken links coming back
Broken internal links are nearly always created by a change: a page renamed, a product removed, a menu edited, a site moved. Catching them at the moment of change is far cheaper than finding them months later.
- Link to pages by selection, not by typing. CMS menu items and link pickers that reference a page by its ID update when the slug changes.
- Check incoming links before deleting. Make it a habit to look up what links to a page before removing it, and repoint those links in the same session.
- Test links in the build. For static and framework sites, a link checker in continuous
integration catches mistakes before they go live. For example,
lychee --offline ./publicchecks links between local files only, without making network requests, and fails with a non-zero exit code if any are broken. - Re-crawl after migrations and redesigns. These are the moments when template links change most. Run the Internal Link Analysis on staging if you can, and again on the live site immediately after launch.
- Keep a friendly 404 page. It will not fix links, but it gives anyone who hits a dead end a search box and links to your main sections.
Pages that have lost every internal link after a clean-up are a related but different problem; the orphan pages guide covers deciding whether to link them back in.
1. Do broken internal links hurt SEO?
They do not bring a penalty, but they do cause real harm. Visitors reach dead ends and often leave. Crawlers spend requests on addresses that return nothing, which matters more on large sites. And any page you meant to support with that link receives no benefit from it. A few broken links on a large site are normal; many broken links from menus or templates suggest the site is not maintained, which is worth fixing for visitors alone.
2. Why does the report show hundreds of broken links when I only deleted one page?
Because links in menus, headers, footers, sidebars and reusable blocks appear on every page that uses them. If one of those contains a link to the deleted page, the crawler finds that broken link on every page of the site and reports each occurrence. Sort the report by destination and you will usually see only a few distinct broken addresses. Fix the menu or template once and all occurrences disappear together.
3. Is a link that goes through a redirect a broken link?
No. It still reaches a working page, so visitors are fine. It is, however, an avoidable hop that slows the journey slightly and makes your internal link data harder to read, because the address in the link is not the page that loads. Since you control your own links, update them to the final address. Keep the redirect itself in place so that bookmarks and links from other websites keep working.
4. Should I redirect every broken address instead of fixing the links?
Redirects are a safety net, not a replacement for fixing links. Add a redirect where a page has moved and outsiders may still use the old address. Then update your own links so they point directly at the new page. Where content is gone with no replacement, do not redirect to the homepage or an unrelated page; remove the internal links, and let the old address return a 404 or 410 with a helpful error page.
5. How do I fix a broken link that appears in my WordPress menu?
Open Appearance, Menus in a classic theme or the Navigation block in the Site Editor for a block theme, and find the item. If it is a custom link with a typed address, replace it with the page itself selected from the list, so future slug changes follow automatically. Save, then clear any caching plugin and CDN cache. If the link is not in the menu, check widgets, template parts, synced patterns and your page builder's global header or footer.
6. What is a safe way to replace one address across the whole site?
On WordPress, use WP-CLI's search-replace command, which handles serialised data correctly, and run it with the dry-run option first to see how many rows would change. Take a full database backup before the real run. Make the search string specific, such as the complete old path, so you do not change unintended text. On static sites, use a project-wide find and replace in your editor and review the changes in version control before deploying.
7. How can I stop broken internal links being published in future?
Link to pages by selecting them in menus and link pickers rather than typing addresses, since those references follow slug changes. Before deleting or renaming any page, check which pages link to it and update them in the same session. On static or framework sites, add a link checker to your build so any broken internal link fails the deployment. Finally, run the Internal Link Analysis after every migration, redesign or large content change.
The Internal Link Analysis maps every internal link across your crawled pages and identifies broken links, orphans and over-linked pages, with the pages each link sits on, so you can trace problems back to their source.
Open Internal Linking Fixes โ