Internal Linking skill for Codex

Route crawlers and readers to the pages that matter. Codex builds the link graph from a crawl export or straight from your Markdown content, measures orphans, click depth and links pointing at redirects or 404s, then adds hubs, breadcrumbs and contextual links toward priority pages with varied descriptive anchors. It finishes with a re-crawl and a broken-link guard in CI.

  • Growth & SEO
  • $internal-linking
  • 160 lines
  • 2 files
  • ~1,750 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- internal-linking

Installs to ~/.agents/skills/internal-linking/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $internal-linking, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Designs and repairs internal link architecture — hub and cluster structure, navigation and breadcrumbs, contextual links with descriptive anchor text, click depth, orphan pages, link equity toward priority pages, and redirecting or broken internal links — using a crawl export or the site's own content files. Use when pages are orphaned or buried, new content is not getting crawled, asked to "add internal links", planning topic clusters or site structure, or after a migration left internal links pointing at redirects.

Use it when

  • New or important pages are not crawled or rank poorly despite good content
  • Planning topic hubs and cluster structure
  • A crawl shows orphans, deep pages or internal links to redirects
  • A navigation redesign may change how pages are linked

Use something else when

How to check the result

A fresh crawl shows zero orphan indexable pages, priority pages within three clicks of home, and no internal links resolving to 3xx or 4xx responses.

The mistake it prevents

Adding the same exact-match anchor from hundreds of footer or sidebar links and expecting it to lift a page, instead of placing relevant contextual links.

~/.agents/skills/internal-linking/SKILL.md

--- name: internal-linking description: Designs and repairs internal link architecture — hub and cluster structure, navigation and breadcrumbs, contextual links with descriptive anchor… license: MIT ---

Done well: every indexable page is reachable through plain <a href> links within a few clicks, priority pages receive the most relevant contextual links, anchors describe the target, and no internal link points at a redirect, a 404 or a non-canonical URL.

Principles

  • Internal links are how Google discovers pages, understands topic relationships and distributes importance. Sitemaps help discovery but do not replace links.
  • Relevance beats volume. Ten links from closely related pages do more than a sitewide footer link.
  • Anchor text is a description of the target for users and search engines: specific and varied (“PgBouncer pool sizing”), never “click here”, never the same exact-match phrase on hundreds of links.
  • rel="nofollow" on internal links wastes discovery; use it only for links you truly do not want crawled (and prefer robots rules or removing the link).
  • Only <a href="..."> links are reliably followed. onclick navigation, <span> buttons and links injected after user interaction are not.

Workflow

- [ ] 1. Get the link graph (crawl export or parse content files)
- [ ] 2. Measure: inlinks, click depth, orphans, broken/redirecting links
- [ ] 3. Define priority pages and topic hubs
- [ ] 4. Fix structural links: nav, hubs, breadcrumbs, pagination
- [ ] 5. Add contextual links between related content
- [ ] 6. Clean up broken, redirecting and non-canonical links
- [ ] 7. Verify and add a guard in CI

Options, best first:

  • Crawler export with an “all inlinks” table (source, target, anchor, link position). The crawl_audit.py script in technical-seo-audit gives depth and inlink counts for small sites.
  • For Markdown/MDX content repos, parse links directly (offline):
import pathlib, re, collections, csv
LINK = re.compile(r"\[([^\]]+)\]\((/[^)\s#]*)")  # internal, root-relative markdown links
edges, pages = [], {}
for f in pathlib.Path("content").rglob("*.md*"):
    slug = "/" + str(f.relative_to("content")).rsplit(".", 1)[0].removesuffix("/index")
    pages[slug] = f
    for anchor, href in LINK.findall(f.read_text(encoding="utf-8")):
        edges.append((slug, href.rstrip("/") or "/", anchor))
inlinks = collections.Counter(t for _, t, _ in edges)
orphans = [p for p in pages if inlinks[p] == 0 and p != "/index"]
with open("links.csv", "w", newline="") as fh:
    csv.writer(fh).writerows([("source", "target", "anchor"), *edges])
print(f"{len(pages)} pages, {len(edges)} links, {len(orphans)} orphans")

Adapt the slug mapping to the site’s routing (check AGENTS.md and the framework’s content config). Add links from templates (nav, footer, related posts) separately, since they are not in the content files.

  • Search Console → Links report shows top internally linked pages (sampled).

2. Measure

MetricTargetWhy
Orphan indexable pages0Unlinked pages are discovered late and rated as unimportant
Click depth of priority pages≤ 3 from homeDeep pages get crawled less
Internal links to redirects0Wastes crawl, dilutes signals, slows users
Internal links to 4040Broken journeys
Links to non-canonical URLs (params, http, trailing-slash variants)0Conflicts with canonical signals
Priority pages’ contextual inlinkstop decile of the siteMatches business priority

3. Priority pages and hubs

  • Priority pages: those that convert or target high-value clusters (from keyword-research). List them; they should be among the most linked pages.
  • Hubs: one page per topic that summarizes it and links to every cluster page; each cluster page links back to its hub and to 2–5 sibling pages where relevant.
  • Don’t create hubs that are only link lists; hubs need their own useful content.
  • Main navigation: top categories and priority pages only; giant mega-menus with hundreds of links flatten everything.
  • Breadcrumbs reflecting the hierarchy, as real links, with BreadcrumbList markup (see schema-markup).
  • Paginated archives with crawlable ?page=N or /page/N links; not only “load more” buttons.
  • Related content blocks driven by real similarity (shared tags, embeddings, co-purchase), not random or “latest posts”.

For each priority page, find relevant mentions on other pages and link them:

# Pages that mention the topic but do not link to the target yet
grep -rliE "connection pool(ing)?|pgbouncer" content/ | xargs grep -L "/guides/pgbouncer-setup"

Rules:

  • Link where the reader would want to go next; the first relevant mention is usually the right spot.
  • Anchor: 2–6 words describing the target; vary naturally across source pages.
  • Keep a reasonable count; there is no hard limit, but a paragraph where every phrase is a link helps no one.
  • Link new content from at least 3 existing, crawled pages at publish time.

6. Clean up

  • Replace links to redirected URLs with the final URL (crawl export: inlinks to 3xx).
  • Fix or remove links to 404s.
  • Normalize to canonical form: protocol, host, trailing slash, no tracking params (utm_* on internal links also corrupts analytics attribution).
# Find hard-coded absolute internal links and http links in content
grep -rnoE 'https?://(www\.)?example\.com[^ )"]*' content/ | head
grep -rnoE '\]\(http://[^)]+' content/ | head

7. Verify and guard

  • Re-crawl: orphans 0, depth targets met, zero internal 3xx/4xx targets.
  • Add a CI check for content repos: fail when a Markdown link targets a slug that does not exist. Many static site generators and docs tools have a broken-link check built in; enable it rather than writing a new one if available.

Gotchas

  • JavaScript-rendered related-post widgets may not be in the server HTML; check curl output, not the browser.
  • Footer/sidebar sitewide links count but carry little contextual relevance; don’t rely on them to boost a priority page.
  • Removing large numbers of internal links (e.g. a nav redesign) can drop rankings of pages that lost their main paths. Diff the link graph before and after redesigns.
  • Faceted navigation can generate millions of crawlable link combinations; link only to indexable facet pages and keep the rest out of <a href> or blocked.
  • Changing anchors sitewide to exact-match keywords looks manipulative and reads badly.

Output

**Graph:** 1,240 pages, 9,860 internal links (content + templates).
**Found:** 37 orphans, 112 links to redirects, 9 to 404s, 14 priority pages at depth ≥ 4.
**Changes:** new hub /postgres/ (links 22 cluster pages); breadcrumbs on /guides/*;
58 contextual links added (list in links-added.csv with source, anchor, target);
121 links rewritten to final URLs.
**After:** orphans 0, max priority depth 3, internal 3xx/4xx 0.
**Guard:** link check in CI (docs build --strict).

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Internal Linking"
  short_description: "Route crawlers and readers to the pages that matter"
  default_prompt: "$internal-linking Find orphan pages in our docs content folder and suggest where to link them from"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem