You published the page. It’s in the sitemap, the copy is good, and six months later it has almost nothing to show for itself. Meanwhile a thinner page you barely thought about is pulling steady traffic.
Before you rewrite anything, check whether the quiet page is linked to from anywhere on your own site. A surprising number of WordPress pages aren’t. They exist at a URL, they sit in the sitemap, and no other page points at them — so Google reached them through a file you handed it rather than through your site’s actual structure, and it has almost no context for what the page is or how much you value it.
Google’s own link documentation is unusually direct about this. Below is what it says, which links Google can and can’t follow, why WordPress manufactures unlinked pages, and how to find yours without opening four hundred edit screens.
The Orphan Page Problem Your Sitemap Hides
Google’s link best practices doc opens by stating what links are for: “Google uses links as a signal when determining the relevancy of pages and to find new pages to crawl.” Two jobs in one sentence — discovery and relevance — and a page nothing links to is working against both. Then, in the internal-links section, comes the line worth pinning above your desk: “Every page you care about should have a link from at least one other page on your site.” That’s about as close to a rule as Google’s documentation gets, and it applies to every page you’d be annoyed to see ranking poorly.
Your XML sitemap doesn’t get you out of it. Google’s sitemap documentation is explicit that a sitemap “helps search engines discover URLs on your site, but it doesn’t guarantee that all the items in your sitemap will be crawled and indexed” — and the crawl-budget docs add that even once a page is crawled, “not every page that is crawled will necessarily be indexed.” The same sitemap page leads with the alternative: “If your site’s pages are properly linked, Google can usually discover most of your site.” Discovery is the easy part. Convincing Google a page matters is the part links do, and a sitemap entry carries no signal about importance, topic, or how the page relates to the rest of your site.
Where WordPress quietly makes orphans
Nobody decides to orphan a page. It happens structurally, and the same few patterns account for most of it:
- Landing pages built for a campaign and deliberately kept out of the menu, then never linked from anywhere else
- Older posts that fell off the paginated blog archive and were never referenced again
- Custom post types registered without archive templates, so the only route in is a direct URL
- Pages whose only inbound link lived in a widget, a sidebar, or a theme you’ve since replaced
- Anything imported during a migration that arrived with its content intact and its context stripped
The blog archive case is the one that catches good sites. Your ten newest posts are linked from the archive’s first page, the next ten from page two, and everything past the last paginated page is reachable only if something else happens to mention it. On a site with three years of posts and no related-posts module, that’s most of your library.
Internal links are among the few ranking inputs entirely in your hands. You can’t make anyone link to you, you can’t make Google print your title verbatim, and you can’t buy your way past a competitor’s domain. But you decide what every page on your own site points at, and you can fix it this afternoon with no budget, no outreach, and no permission from anyone.
The Links Google Can’t Follow
Before worrying about which pages to link, make sure the links you already have are the kind Google can use. This is narrower than most people assume.
It has to be an anchor element with an href
Google’s requirement is specific: “Generally, Google can only crawl your link if it’s an <a> HTML element (also known as anchor element) with an href attribute. Most links in other formats won’t be parsed and extracted by Google’s crawlers.” The docs then list the patterns filed under “not recommended” — Google may still attempt to parse some of them, but you can’t count on it — and they’re all things you’ll find in real themes and page builders:
<a routerLink="products/category">— a framework attribute instead of an href<span href="https://example.com">— the right attribute on the wrong element<a onclick="goto('https://example.com')">— a click handler with no href at all
The href’s value matters too. Google wants something that resolves to an actual web address it can send a request to, so /products, /products.php?id=123, and a full https:// URL are all fine. Filed under not recommended, with the same “Google may still attempt to resolve this” caveat, is the JavaScript-in-the-href pattern: href="javascript:goTo('products')" and its cousins, which look and behave like links for a mouse user while giving a crawler nothing dependable to follow.
JavaScript is fine, until it isn’t
This trips people who’ve heard “Google can’t read JavaScript” and stopped there. The docs are clear that “links are also crawlable when you use JavaScript to insert them into a page dynamically as long as it uses the HTML markup shown above.” Render a real <a href> into the DOM and it counts; render a <div> with a click handler and you’re back in not-recommended territory. The distinction is the markup you end up with, not the language that produced it.
We audited a site last year where the blog listing had a “Load more” button instead of pagination. It looked modern, it worked fine for humans, and it injected post cards as <div> elements with click handlers. The nine posts above the fold were linked; the other two hundred had nothing dependable pointing at them. Nobody had touched the content in a year, because the content wasn’t the problem. Swapping the cards to real anchors and adding numbered pagination underneath the button fixed in one afternoon what a year of rewriting hadn’t. Google’s own advice on the JavaScript case is to check rather than assume: if you’re inserting anchor text with JavaScript, use the URL Inspection Tool in Search Console and confirm it’s present in the rendered HTML — the same check works for JS-inserted links.
Anchor Text Is the Other Half of the Signal
A crawlable link tells Google the page exists; the anchor text tells it what the page is about. Google defines anchor text (also called link text) as the visible text of a link, and describes good anchor text as descriptive, reasonably concise, and relevant both to the page it’s on and the page it points to. The docs single out the phrases everyone defaults to — “Click here,” “Read more,” and a bare “website” or “article” as the linked words are all shown under “Bad (too generic).” There’s a test in the documentation that beats any rule: read only the anchor text, out of context, and see whether it’s specific enough to make sense by itself. If you can’t tell what the destination is, neither can Google.
Overcorrecting has its own failure mode. Google’s example of bad anchor text isn’t only the vague kind — it’s also a link that swallows an entire sentence, and cramming keywords into anchor text is explicitly a violation of the spam policies. Somewhere between “click here” and a thirty-word run-on is a short descriptive phrase that names the destination.
The details people miss
Three smaller points from the same doc:
- For an image used as a link, Google uses the
altattribute of theimgelement as the anchor text. An image link with empty alt is a link with no anchor text. - If an
<a>element is empty for some reason, Google can fall back to thetitleattribute as anchor text. Useful for icon links, though real text is better. - Don’t chain links next to each other. The docs point out it’s harder for readers to tell them apart, and you lose the surrounding text that gives each one context — the words before and after a link matter.
On how many links a page should have, the guidance is refreshingly unmathematical: “There’s no magical ideal number of links a given page should contain. However, if you think it’s too much, then it probably is.”
Crawlable: an <a> element with an href that resolves to a real address · JavaScript-inserted links using that same markup.
Not recommended (Google may attempt to parse these, but don’t rely on it): <span href> · <a> with only an onclick · href="javascript:..." · framework attributes like routerLink.
Anchor text: descriptive and concise · readable out of context · image links use alt · empty anchors fall back to title · no keyword stuffing · don’t chain links together.
The rel Attributes, and What WordPress Does Behind Your Back
Google documents three rel values for qualifying a link, and for ordinary links you expect Google to fetch and parse normally you don’t need any of them.
rel="sponsored"for advertisements and paid placements. The docs note thatnofollowwas previously recommended here and is still acceptable, thoughsponsoredis preferred.rel="ugc"for user-generated content such as comments and forum posts.rel="nofollow"when the other values don’t apply and you’d rather Google not associate your site with the linked page.
You can combine them as a space- or comma-separated list. Two caveats matter. Links carrying these attributes “will generally not be followed,” but the linked pages “may be found through other means, such as sitemaps or links from other sites, and thus they may still be crawled” — this isn’t a blocking mechanism. And for links within your own site, Google points you at the robots.txt disallow rule instead; sculpting your internal link graph with nofollow is not what that attribute is for.
Core is already tagging your comment links
WordPress handles a chunk of this for you, which is worth knowing before you install a plugin to do it again. Since version 5.3, core’s wp_rel_ugc() adds rel="nofollow ugc" to anchor elements inside comment content, hooked to pre_comment_content — so it’s applied when the comment is saved, not at render time. The commenter’s own URL gets handled separately in get_comment_author_link(), which includes ugc and adds external and nofollow when the URL isn’t internal to your site; since WordPress 6.2 a comment_author_link_rel filter lets you change that for trusted contributors.
One piece of old advice to retire: wp_targeted_link_rel(), the function that appended rel="noopener" to links with a target (and, before 5.6, noreferrer alongside it), is marked deprecated as of WordPress 6.7 in core source. If you’re carrying a snippet that hooks it, that snippet is now maintaining something core no longer wants maintained.
Auditing Internal Links Without Losing a Week
Reviewing links page by page is where this project dies. On a 400-page site nobody opens 400 editors, so treat it as a data problem instead. Start with two lists: your sitemap gives you every URL that’s supposed to exist (/wp-sitemap.xml on core since WordPress 5.5, or your SEO plugin’s equivalent if it replaced the core one), and a crawl of your own site starting at the homepage gives you every internal URL something actually links to. Diff the two, and anything present in the sitemap but absent from the crawl is an orphan. That list is usually shorter and more actionable than people expect.
For inventory on the WordPress side, WP-CLI beats any admin screen: wp post list --post_type=post,page --post_status=publish --fields=ID,post_title,post_name hands you the full set in one command, ready to sort against your crawl.
Fix templates before you fix pages
Most orphans come from a missing template feature rather than a missing sentence, so template-level fixes clear them in bulk while page-level fixes clear one at a time. Related posts underneath a single post, a breadcrumb trail, category and tag archives that are actually linked from somewhere, real numbered pagination on the blog — each of those puts hundreds of pages back on the map at once. If your theme renders breadcrumbs, mark them up as BreadcrumbList structured data too, so the hierarchy is machine-readable rather than merely visible; that’s one of the schema types amplifi.schema generates automatically across a site.
Then do the contextual pass by hand, because that’s the part templates can’t fake. When you write a new post, link to the two or three older pieces it genuinely builds on, then go back to those older pieces and link forward to the new one. Cluster your content around the few pages you most want to rank and let the cluster point inward. That’s an hour a week that compounds.
amplifi.schema ships inside the amplifi.plugins suite — a single free, open-source WordPress plugin bundling our AI SEO, schema, alt-text, consent, and security features. One install, one update, everything toggleable. Full source on GitHub.
View on GitHubWhatever you change, verify it in the served HTML rather than in a preview. Fetch the live URL, look at the markup, and confirm your links are real anchors with real hrefs and real text between them — page builders and JavaScript-heavy themes are perfectly capable of showing you a link in the editor and shipping something else to the crawler.
Frequently Asked Questions
Not sure how many of your pages nothing links to? We’ll diff your sitemap against a real crawl and hand you the list.
Built by amplifi.studio — see also Google Can’t Index What It Can’t Find: A WordPress XML Sitemap Guide.