Technical SEO

Internal Linking with Claude Code: Find and Fix Orphan Pages

Use Claude Code and a free Python script to count internal links, find orphan pages, add relevant links in your content files, and verify before you publish.

By Somanath Diksangi8 min read

Internal links are how Google finds your pages and understands which ones matter. If nothing links to a page, Google may never crawl it, however good it is. Claude Code is a good tool for fixing this because it can do both halves of the job: measure your internal links with a script, then edit your content files to add the missing ones.

This guide gives you a ready-to-run script that finds orphan pages, the prompts to turn its output into link suggestions, and the checks to run before you publish.

  • Discovery. Google finds most pages by following links. Pages with no internal links (orphan pages) are crawled rarely or not at all.
  • Context. The anchor text and surrounding sentence tell Google what the linked page is about.
  • Priority. Pages linked from your homepage and main navigation are treated as more important than pages buried five clicks deep.

We learned this on our own site. Our 12 feature pages were only linked from each other, weren't in the sitemap, and loaded their content with JavaScript. Google hadn't indexed a single one. Once they became normal pages linked from the homepage, footer, skills page, and blog posts, they were finally crawlable.

This Python script reads your sitemap, fetches every page, and counts how many other pages link to each one. It only uses Python's standard library, so there's nothing to install. Ask Claude Code to save it as scripts/internal_links.py, or paste it in yourself:

python
"""Count internal links pointing at every page in a sitemap and flag orphan pages.

Usage: python3 internal_links.py https://www.example.com
       python3 internal_links.py http://localhost:3000   (test a local build before deploying)
"""
import re
import sys
import urllib.request
from html.parser import HTMLParser
from urllib.parse import urljoin, urlparse

SITE = sys.argv[1].rstrip("/")


class LinkParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.hrefs = []

    def handle_starttag(self, tag, attrs):
        if tag == "a":
            href = dict(attrs).get("href")
            if href:
                self.hrefs.append(href)


def fetch(url):
    request = urllib.request.Request(url, headers={"User-Agent": "internal-link-check"})
    with urllib.request.urlopen(request, timeout=20) as response:
        return response.read().decode("utf-8", "ignore")


def path_of(url):
    return urlparse(url).path.rstrip("/") or "/"


locs = re.findall(r"<loc>(.*?)</loc>", fetch(f"{SITE}/sitemap.xml"))
own_hosts = {urlparse(SITE).netloc, urlparse(locs[0]).netloc}
inbound = {path_of(loc): set() for loc in locs}

for page_path in inbound:
    parser = LinkParser()
    parser.feed(fetch(SITE + page_path))
    for href in parser.hrefs:
        target = urljoin(SITE + page_path, href)
        if urlparse(target).netloc not in own_hosts:
            continue
        target_path = path_of(target)
        if target_path in inbound and target_path != page_path:
            inbound[target_path].add(page_path)

for path, sources in sorted(inbound.items(), key=lambda item: len(item[1])):
    flag = "  <- ORPHAN" if not sources else ""
    print(f"{len(sources):3d}  {path}{flag}")

Run it against your live site, or against a local build before you deploy:

bash
python3 scripts/internal_links.py https://www.your-site.com
python3 scripts/internal_links.py http://localhost:3000

The output lists every page with its number of internal links, weakest first. Here's the top of the report from our own site:

text
  2  /features/seo/local-seo
  4  /blog/generative-engine-optimization-guide
  4  /features/blog/content-templates
  4  /features/blog/search-ai-readiness
  5  /features/seo/eeat-content

No orphans, but the Local SEO page had only two links pointing to it, the fewest on the site. That's the first page to fix.

Step 2: Let Claude Code read the report and your content

Now give Claude Code the report and ask it to find real link opportunities, not just any keyword match:

text
Run scripts/internal_links.py against http://localhost:3000 and read the output. For the 5 pages with the fewest internal links, find existing pages in this repo that discuss the same topic and would genuinely help a reader to link from. For each suggestion, show the source file, the exact sentence where the link fits, and a descriptive anchor text. Don't suggest links from unrelated pages.

Ask for the exact sentence. It forces suggestions that make sense in context instead of a list of pages with matching words.

Once you've reviewed the suggestions, ask Claude Code to make the edits:

text
Add the approved links to the source files. In Markdown posts, use normal Markdown links. In components, use the framework's link component (for example next/link in Next.js) instead of a plain anchor tag. Don't change any other text.

Where your links live affects how you add them:

Where the link goesHow to add it
Blog posts (Markdown/MDX)Inline link in a relevant sentence
Product or feature pagesA "related" section or a link in the body copy
HomepageA section linking your most important pages and newest guides
Navigation and footerYour top-level pages only, not every post

Step 4: Verify before you publish

Build the site locally, run the script again, and compare:

text
Build the site, start it locally, and run scripts/internal_links.py again. Compare the new counts with the previous run and confirm every page we targeted gained links. Also check that none of the new links return a 404.

Rerun the script whenever you publish a new page. A new post starts with zero internal links, so link to it from at least two or three related pages on the day it goes live.

Anchor text rules

DoDon't
Describe the destination: "our technical SEO audit guide"Use vague anchors: "click here", "read more"
Vary the wording naturally across pagesUse the identical keyword anchor on dozens of pages
Link where a reader would actually want more detailStuff several links into one sentence
Link to the canonical, indexable version of a pageLink to redirects, noindexed pages, or URLs with tracking parameters

Common internal linking mistakes

  • Footer-only links. Sitewide footer links help discovery, but links in the main content carry more context. Don't rely on the footer alone.
  • Links that only exist after JavaScript runs. Menus or "related posts" widgets that render only in the browser may not be seen when Google first processes the page. Check that important links are in the raw HTML.
  • Linking to pages you've blocked. A link to a noindexed or robots-blocked page wastes the link. Fix the target or remove the link.
  • Never updating old posts. Your oldest posts usually have the most authority. Add links from them to your newer pages.

Doing this with ClaudeSEO

The full site audit in the Claude SEO skill checks internal linking gaps as part of its on-page review, alongside technical, content, and schema checks. The technical SEO audit covers crawlability and indexability, the problems that most often hide orphan pages. See every command on the Claude SEO skill list.

For a full audit workflow, read how to run a technical SEO audit with Claude Code. For ready-made prompts, see our 25 Claude Code SEO prompts.

FAQ

There's no fixed number. Every important page should have several contextual links from related pages, and no page you want indexed should have zero. Add links where they help the reader, not to hit a count.

What is an orphan page?

An orphan page is a page that no other page on your site links to. Google can only find it through your sitemap or external links, so it's often crawled rarely and can be slow to get indexed.

Yes. Claude Code can find link opportunities across your content and edit the files for you. Review its suggestions before you approve them, so every link makes sense to a reader and points to the right page.