Crawlable Links: Real hrefs and Descriptive Link Text
Search engines discover most pages by following links. Google’s crawler extracts URLs from <a>
elements that have an href attribute. It does not click buttons, run click handlers or guess
where a script would navigate. A link that only works through JavaScript is, for a crawler, not a
link at all.
What counts as a crawlable link
Google’s documentation is explicit about what it can follow:
<a href="/pricing">Pricing</a>
<a href="https://example.com/guides/trail-shoes">Trail shoe guide</a>
<a href="/blog/" onclick="trackClick()">Blog</a>
And what it cannot:
<a onclick="goTo('/pricing')">Pricing</a>
<a href="javascript:goTo('/pricing')">Pricing</a>
<span class="link" data-href="/pricing">Pricing</span>
<button onclick="location.href='/pricing'">Pricing</button>
Adding JavaScript on top of a real href is fine: the crawler uses the URL, visitors get your
script. The problem is links whose only destination lives in code.
Fragment-only links (href="#") are a related trap. They point back at the same page, so a menu
built from <a href="#"> items with click handlers gives crawlers nothing to follow.
Why it matters
- Pages are not found. If the only path to a category or article is a JavaScript-driven menu, crawlers may never discover it, or find it only through the sitemap, without the context that internal links provide.
- Internal links pass context. Links tell search engines which pages you consider important and what they are about. Links they cannot see do neither.
- Accessibility and usability. Real links work with the keyboard, middle-click, “open in new tab”, and screen readers announce them as links. Scripted pseudo-links often break all of these.
Descriptive link text
The text inside a link (its anchor text) is one of the main signals for what the target page is about, and screen-reader users often navigate by listing all links on a page. A list of ten “Read more” links helps neither.
Lighthouse flags generic phrases such as “click here”, “here”, “read more”, “more”, “learn more” and “start”. Rewrite them so the link makes sense on its own:
<!-- Before -->
<p>Our team plan includes SSO. <a href="/pricing/teams">Click here</a>.</p>
<!-- After -->
<p>Single sign-on is included in the <a href="/pricing/teams">team plan</a>.</p>
For card layouts where a “Read more” button is part of the design, make the card title the link, or keep the visible text short and add context that screen readers announce:
<a href="/blog/http3">Read more<span class="visually-hidden"> about HTTP/3</span></a>
How to fix
- Find scripted navigation. In DevTools, search the Elements panel for
onclick=,javascript:andhref="#". Single-page apps should use their router’s link component (<Link>,<RouterLink>,<a>withhref), which renders a real anchor. - Give every menu item a URL. Dropdown toggles can stay buttons, but each item inside should
be an
<a href>. - Paginate with links. “Load more” buttons and infinite scroll hide later pages; add
paginated URLs (
?page=2) linked with real anchors as well. - Rewrite generic link text in templates first (cards, excerpts, footers), where one change fixes many pages.
In WordPress, the “Read more” text of excerpts usually comes from the theme’s excerpt_more
filter or a block setting; changing it there fixes every archive page at once.
How PerfoMetrix checks this
We read two Lighthouse audits from the PageSpeed Insights run the scan already makes:
crawlable-anchors for crawlable links and link-text for descriptive text. Google loads the page
once in a mobile lab browser; we send no extra requests to your site. Either check needs attention
when Lighthouse flags at least one link, and the report lists the first few. Lighthouse sees the
page as rendered on load, so links that only appear after interaction are not tested.
Further reading: Google: link best practices and Lighthouse: link text.