Orphan pages internal linking opportunities
Skill Infrasity-Labs/dev-gtm-claude-skills/skills/orphan-pages-internal-linking-opportunities
Audit any domain for orphan blog pages, map internal links across all blog posts, generate keyword-backed interlinking briefs via DataForSEO, and produce a self-contained downloadable HTML report. Invoke with the target domain as the argument.From its SKILL.md
npx -y skills add Infrasity-Labs/dev-gtm-claude-skills --skill orphan-pages-internal-linking-opportunitiesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
11.0 KB, ~2.7k tokens by cl100k_base, as published. Nobody here has run it
Orphan Page Audit & Interlinking Brief Generator
You are running a complete SEO orphan page audit. The user has provided a domain as input via args. Your job is to execute every phase below in order, using the tools available, and produce a single downloadable HTML report file saved to the current working directory.
Input domain: {args}
PHASE 1 — Domain Normalization & Sitemap Discovery
-
Normalize the input domain:
- Strip leading/trailing whitespace
- If it doesn't start with
http, prependhttps:// - If it doesn't have
www.and the base domain resolves with it, usewww. - Remove any trailing slash
- Store as
BASE_URL(e.g.,https://www.example.com)
-
Fetch
BASE_URL/robots.txtusing WebFetch. Look for anySitemap:directive — if found, use that URL as the sitemap location. If not found, default toBASE_URL/sitemap.xml. -
Fetch the sitemap URL. Determine its type:
- Sitemap index (
<sitemapindex>tag present): extract all<loc>child sitemap URLs, fetch each one, and merge all<loc>URLs from every child sitemap into a single master URL list - Regular sitemap (
<urlset>tag): extract all<loc>URLs directly
- Sitemap index (
-
Store the full master URL list.
PHASE 2 — Blog URL Identification
From the master URL list:
-
Analyze all URL patterns to identify the blog/content section. Look for the URL prefix that appears most frequently in a uniform pattern. Common prefixes to check:
/blog/,/articles/,/posts/,/insights/,/resources/,/learn/,/news/,/guides/ -
Select the prefix that best represents the blog content (most URLs, most uniform structure). If multiple prefixes qualify, include all of them.
-
Filter the master URL list to only include URLs matching the identified blog prefix(es). Exclude index/listing pages (e.g.,
BASE_URL/blogwith no further slug). Store asBLOG_URLS. -
If no blog prefix is identifiable from the sitemap, fetch
BASE_URLhomepage and look for a blog/articles navigation link, then fetch that page and extract all post URLs from it. -
Log the total count: "Found {N} blog posts at {BASE_URL}"
PHASE 3 — Crawl & Build Inbound Link Map
Run a Workflow to crawl all blog posts in parallel. The workflow should:
Script logic:
- Take
BLOG_URLSas the items list - For each URL, spawn an agent that:
- Fetches the page using WebFetch
- Extracts ALL href links in the body/article content (not nav, header, footer) that point to other pages on the same domain matching the blog URL pattern
- Normalizes all found links to absolute URLs (no trailing slash, no query params, no hash)
- Returns
{ source_url, outbound_blog_links: [...] }
- Use schema:
{ type: 'object', properties: { source_url: { type: 'string' }, outbound_blog_links: { type: 'array', items: { type: 'string' } } }, required: ['source_url', 'outbound_blog_links'] }
After the workflow completes, aggregate results:
- Build
inbound_countmap:{ url -> number }initialized to 0 for allBLOG_URLS - Build
inbound_sourcesmap:{ url -> [source_url, ...] }initialized to[]for allBLOG_URLS - For each crawl result, iterate
outbound_blog_linksand increment the inbound count + push the source URL for each valid link
Classify every page:
- Orphan = 0 inbound links → store as
ORPHAN_URLS - Low-linked = 1–2 inbound links → store as
LOW_LINKED_URLS - Healthy = 3+ inbound links → store as
HEALTHY_URLS
Build FULL_INBOUND_MAP: array of { url, inbound, linked_from } sorted descending by inbound count.
PHASE 4 — Keyword Research for Orphan Pages
Run a Workflow to get the top US search volume keyword for each orphan page. The workflow should:
Script logic:
- Take
ORPHAN_URLSas the items list - For each orphan URL, spawn an agent that:
- Extracts the slug from the URL (last path segment)
- Generates 6–8 keyword variants from the slug (convert hyphens to spaces, try variations with different word orders, add/remove qualifiers)
- Uses ToolSearch to load
mcp__claude_ai_DataForSEO__kw_data_google_ads_search_volume, then calls it with the keyword variants,location_code: 2840(United States),language_code: "en" - Picks the keyword with the highest US monthly search volume as
anchor_text - Fallback: if DataForSEO returns no data or errors, convert the slug to a phrase (hyphens → spaces) as the
anchor_text - Fetches the page and writes a 2–3 sentence
page_summaryof what the page covers - Returns
{ orphan_url, anchor_text, us_monthly_volume, page_summary }
- Use schema:
{ type: 'object', properties: { orphan_url: { type: 'string' }, anchor_text: { type: 'string' }, us_monthly_volume: { type: 'number' }, page_summary: { type: 'string' } }, required: ['orphan_url', 'anchor_text', 'us_monthly_volume', 'page_summary'] }
Store results as KEYWORD_DATA: map of { orphan_url -> { anchor_text, us_monthly_volume, page_summary } }
PHASE 5 — Generate Interlinking Briefs for Orphan Pages
Run a Workflow using a 2-stage pipeline over ORPHAN_URLS:
Stage 1 — Pass through keyword data (already computed, no agent needed — use the KEYWORD_DATA directly)
Stage 2 — For each orphan, spawn an agent that:
- Receives
orphan_url,anchor_text,page_summary, and the fullBLOG_URLSlist - Selects exactly 3 topically relevant source pages from
BLOG_URLS(exclude the orphan itself; prefer well-linked, non-orphan pages) - Fetches each of the 3 source pages using WebFetch
- For each source page produces:
source_url: the full URL of the source pagewhere_to_place: specific, precise description of the location within that page — naming the H2/H3 section, which paragraph, what content surrounds it. Must be specific enough that a content editor can navigate there without guessingcontext_copy: minimum 300 characters of naturally flowing copy to be inserted at that location. Theanchor_textMUST appear exactly once within it as<a href="ORPHAN_URL">ANCHOR_TEXT</a>. Copy must read as if it was always part of the article — not promotional, not forced. Same anchor text across all 3 placements.
- Returns
{ orphan_url, anchor_text, placements: [{ source_url, where_to_place, context_copy }, ...] }
- Use schema with
placementsas array of 3 objects each requiringsource_url,where_to_place,context_copy
After the workflow completes, post-process every context_copy:
- Check if
anchor_textappears as a hyperlink (href="orphan_url"present) - If not, find the first occurrence of
anchor_text(case-insensitive) and replace with<a href="orphan_url">anchor_text</a>
Store as BRIEFS.
PHASE 6 — Generate HTML Report
Using the Write tool, save a single self-contained HTML file to the current working directory named orphan-audit-{domain}-{YYYYMMDD}.html where {domain} is the base domain (no protocol, no www, dots replaced with hyphens) and {YYYYMMDD} is today's date.
The HTML file must be completely self-contained — all CSS inline in a <style> tag, no external CDN or font imports, works offline. Use clean, professional design with the following structure:
HTML Report Structure
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Orphan Page Audit — {domain}</title>
<style>
/* Full inline CSS — professional design, light background, clean typography */
/* Use system fonts: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif */
/* Color palette: #0f172a (headings), #334155 (body), #3b82f6 (accent/links), #f1f5f9 (backgrounds), #e2e8f0 (borders) */
/* Stat cards, tables, collapsible sections, copy buttons */
</style>
</head>
<body>
Section 1 — Header
- Site logo area with domain name (large)
- Audit date
- "Download Report" button (triggers
window.print()or direct file save)
Section 2 — Executive Summary Four stat cards in a row:
- Total Blog Posts Found:
{BLOG_URLS.length} - Orphan Pages:
{ORPHAN_URLS.length}+ percentage of total - Low-Linked Pages:
{LOW_LINKED_URLS.length}(1–2 inbound links) - Healthy Pages:
{HEALTHY_URLS.length}(3+ inbound links)
A horizontal bar showing the proportion of orphan / low-linked / healthy visually.
Section 3 — Orphan Pages List
Table with columns: # | Page URL (clickable link) | Anchor Text | US Monthly Volume
One row per orphan page.
Section 4 — Inbound Link Map (Non-Orphan Pages)
Table sorted descending by inbound count: Page URL | Inbound Links | Linked From
For the "Linked From" column, list each source URL on a new line or as a comma-separated list.
Section 5 — Interlinking Briefs For each orphan page, a card containing:
- Orphan URL (h3, clickable)
- Anchor Text (badge/pill)
- US Monthly Search Volume
- Three placement blocks, each showing:
- Source page URL (bold, clickable)
- "Where to Place" — displayed as a styled blockquote or info box
- "Context Copy" — displayed in a styled
<pre>or<div>with a "Copy" button that usesnavigator.clipboard.writeText()to copy the HTML
Section 6 — Footer
- "Generated by Infrasity Orphan Page Audit Skill"
- Audit date and domain
JavaScript (inline <script> tag)
- Copy button functionality:
navigator.clipboard.writeText(element.innerText) - Smooth scroll for any nav links
- Optional: collapse/expand for brief cards
PHASE 7 — Confirm & Report to User
After saving the HTML file:
- Report the file path to the user
- Give a brief summary:
- Total blog posts found
- Number of orphan pages
- Number of interlinking briefs generated
- File name and location
- Note any pages where keyword data could not be fetched from DataForSEO (fell back to slug)
Important Rules
- Never hardcode
/blog/— always detect the URL pattern from the actual sitemap - Anchor text is fixed per orphan — the same
anchor_textstring appears in all 3context_copyblocks for that orphan, always as a hyperlink - Context copy minimum 300 characters — enforce strictly; if an agent returns shorter copy, flag it
- Where to place must be specific — section heading + paragraph position + surrounding content context
- HTML report is self-contained — no external requests, no CDN, works offline
- DataForSEO fallback — if API returns no data, use the slug converted to a readable phrase
- Post-process hyperlinks — after all briefs are generated, always verify and fix anchor text hyperlinks before writing the HTML
- File naming —
orphan-audit-{domain}-{YYYYMMDD}.htmlsaved in the user's current working directory
What ships with it: 2 files
38.5 KB alongside SKILL.md
references/
- report-style-reference.html30.5 KB
- README.md8.0 KB