Internal links: help visitors and crawlers find pages
How internal links work for visitors and search engines: link text, link counts, orphan pages, and a script that shows click depth and broken links.
In this guide
What internal links do
Lighthouse explains the crawler side. Search engines may use href attributes on links to crawl a site, and the href should point to an appropriate destination so that more pages can be discovered. A page that nothing links to can be missed, even if it is in your content system.
Link text does a second job. Lighthouse's documentation says that when link text clearly conveys the target, both users and search engines can more easily understand your content and how it relates to other pages. It adds: pay attention to your internal links too. Improving their quality can help both users and search engines navigate your site.
MDN makes the same point from the accessibility side. Assistive software can list all the links on a page, and strong link text benefits all users, because that list works like the way sighted visitors scan a page.
The Almanac's view is balanced. Links are still a crucial signal, it says, but the days of more links simply meaning higher rankings are largely over. Link signals now sit beside content quality, relevance and user experience.
How many links a page has
In the 2025 Web Almanac, the median page had 43 links to its own site on desktop and 39 on mobile. At the 90th percentile the figures were 174 and 161. The Almanac links the high end to large sites with mega-menus and dense footers. External links were far fewer: a median of six, and 25 or 24 at the 90th percentile.
Menus and footers repeat on every page, so they make up much of the count. A long menu is also part of the page's markup. See DOM size. A short, clear menu that repeats everywhere, plus links inside the text to related pages, serves visitors and crawlers better than a menu that lists everything.
Write link text that works
Lighthouse flags these generic phrases: "click here", "click this", "go", "here", "this", "start", "right here", "more" and "learn more". Its advice is to replace them with specific descriptions that tell the reader what they will get.
| Weak link text | Strong link text |
|---|---|
| "click here", in "To see our guides on images, click here." | "guides on images", in "Read our guides on images." |
| "learn more" | "how responsive images work" |
| "https://example.com/pricing/" | "how we scope a project" |
More of Lighthouse's rules:
- Stay on topic. Link text should relate to the page it leads to.
- Do not use the page's address as the text, unless you are mentioning a new address.
- Keep it short: a few words, or a short phrase.
- Rewrite the sentence if you need to. The link should read naturally.
In the Almanac, the share of pages passing Lighthouse's link text check in 2025 was 84.64% of home pages on desktop and 86.64% on mobile, and 92.42% and 93.45% for inner pages. Home pages score lower, the Almanac says, probably because of generic calls to action. An image that is a link needs alt text that names the action. See image SEO and speed.
Use real links
A link needs an a element with an href. A span or button that changes the page with script gives crawlers no address to follow. Lighthouse 13.5 flags javascript:void(0) links and anchors with no href that rely on a click handler. See JavaScript and SEO.
Give every page a path
A page with no links pointing to it is an orphan. The other measure is click depth: how many clicks it takes to reach a page from the home page. Both are easy to measure by following links, as a crawler would. This script does that on one site. It needs only Node 18 or later:
// crawl.mjs: node crawl.mjs https://example.com/ [maxPages]
const start = new URL(process.argv[2]);
const max = Number(process.argv[3] || 300);
const pages = new Map([[start.href, { depth: 0, inbound: 0, status: 0, from: new Set() }]]);
const queue = [start.href];
while (queue.length && pages.size <= max) {
const url = queue.shift();
const page = pages.get(url);
const res = await fetch(url, { redirect: 'manual' }).catch(() => null);
page.status = res ? res.status : 0;
if (!res || res.status !== 200 || !(res.headers.get('content-type') || '').includes('text/html')) continue;
const html = (await res.text()).replace(/<(script|style)[\s\S]*?<\/\1>/gi, ' ');
for (const m of html.matchAll(/<a\s[^>]*href=["']([^"']+)["']/gi)) {
let link;
try { link = new URL(m[1], url); } catch { continue; }
if (link.origin !== start.origin) continue;
link.hash = '';
const known = pages.get(link.href);
if (known) { known.inbound++; known.from.add(url); }
else { pages.set(link.href, { depth: page.depth + 1, inbound: 1, status: 0, from: new Set([url]) }); queue.push(link.href); }
}
}
const path = (u) => u.replace(start.origin, '') || '/';
const all = [...pages];
console.log(all.length, 'addresses found; deepest page is', Math.max(...all.map(([, p]) => p.depth)), 'clicks from the start');
for (const [u, p] of all.filter(([, p]) => p.status >= 300)) {
console.log(p.status >= 400 || p.status === 0 ? 'BROKEN ' : 'REDIRECT', path(u), '<- linked from', [...p.from].slice(0, 3).map(path).join(', '));
}
console.log('fewest links in:', all.filter(([, p]) => p.status === 200).sort((a, b) => a[1].inbound - b[1].inbound).slice(0, 3).map(([u, p]) => `${path(u)} (${p.inbound})`).join(', '));I tested it on a small made-up site with a redirect, a missing page, a chain of pages three clicks deep and one page that nothing links to. It found seven addresses, named the redirect and the broken link with the page that links to each, and reported the depth of three. It did not find the orphan, which is how you know it is one: compare the pages it found with the full list of pages from your content system or your sitemap. Any page on the list that the crawl did not reach needs a link. I also ran it on a local build of 120 pages. It found them all, the deepest was two clicks from the start, and there were no redirects or broken links.
The script is simple. It reads only the HTML that the server sends, and it does not run scripts. If your menus are built by script, it will not see them, and neither might a crawler. See JavaScript and SEO.
Shape the structure
- Hubs. A topic page that links to every guide on the topic, and guides that link back to it, gives crawlers and visitors a clear route.
- Links in the text. A link inside a paragraph, placed where the reader needs the other page, is worth more than a pile of links in a footer.
- Related links at the end of a page help a reader who finished and wants more.
- Breadcrumbs show where a page sits. They can also be written as structured data. See structured data basics.
- Few clicks. Put the pages that matter most close to the home page.
Links to other sites
The Almanac found noopener on 40.9% of pages and noreferrer on 25%. Neither affects ranking, it says: they protect visitors. MDN notes that target="_blank" already provides the noopener behavior. For search, nofollow was on 32.3% of pages, while the finer sponsored and ugc values were on only 0.5% each. MDN describes nofollow as telling search engines to ignore the link relationship, and says it may mean the page's owner does not endorse the target.
The speed side
- Redirected links. MDN advises that if you can avoid a redirect by correcting internal links, fix the links. Each redirect costs a round trip. See redirects and status codes.
- Broken links waste a request and leave the visitor with nothing.
- Real links enable preloading. Browsers can prefetch or prerender a page the visitor is likely to open next, from the links on the current page. See speculation rules.
- Menus add markup. Keep the menu and footer lean.
How to check
- Run the crawl script on your home page, and compare its list with your full list of pages.
- Read the report for redirects and broken links, and change each link to the final address.
- Run Lighthouse and read Links have descriptive text and Links are crawlable.
- Read your main menu and footer as a list. Would a stranger know where each one goes?
- Open your most important page and ask where a reader goes next. If there is no link, add one.
Mistakes to avoid
- Orphan pages, only reachable from a sitemap or by typing the address.
- "Click here" and "learn more".
- The address as the link text.
- Navigation built from `span` or `button` elements.
- Internal links to redirected addresses, such as the old address after a move.
- Links to deleted pages.
- Dozens of links in a footer that repeat on every page.
- Different words for the same link, and the same words for different links.
Check your own page
Run the free audit and read the SEO findings on links. Then run the crawl script on your home page, and write down the three pages with the fewest links pointing to them.
Sources
- The Lighthouse documentation on link text, the Lighthouse 13.5 crawlable anchors audit text, and Redirections in HTTP on MDN.
- SEO chapter, Web Almanac 2025: the Links section, with numbers of links, descriptive link rates and
relattribute use. - MDN: the anchor element, the rel attribute and rel="noopener".
Start here
Free website audit
Live Google data for any public page: mobile and desktop.
Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it
Rather have this done for you? Technical SEO · Book a call