Fix Uncrawlable Links for Better SEO

Learn how to fix anchor elements that search engines can't follow due to missing or invalid href attributes.
Harlan WiltonHarlan Wilton4 min read Published

Search engines discover your pages by following links. When your anchor elements don't have proper href attributes, Googlebot can't crawl them—and those pages won't get indexed.

What's the Problem?

Modern JavaScript frameworks make it easy to handle clicks without real links. But if there's no valid href, search engines see a dead end.

Lighthouse flags these patterns as uncrawlable:

Empty or missing href:

<a onclick="navigate('/about')">About</a>
<a href="">About</a>
<a href="#">About</a>

JavaScript pseudo-URLs:

<a href="javascript:void(0)">About</a>
<a href="javascript:">About</a>

Invalid URLs:

<a href="file:///path/to/file">Download</a>

Click handlers without href:

<a @click="goToAbout">About</a>

Googlebot does execute JavaScript, but it doesn't click on elements. It follows href attributes. No href, no crawl.

How to Identify This Issue

Chrome DevTools

  1. Open DevTools (F12) → Elements tab
  2. Search for <a and manually inspect href attributes
  3. Or run this in the Console:
document.querySelectorAll('a').forEach((a) => {
  const href = a.getAttribute('href')
  if (!href || href === '#' || href === '' || href.startsWith('javascript:')) {
    console.log('Uncrawlable:', a.outerHTML)
  }
})

Lighthouse

Run a Lighthouse SEO audit. Look for "Links are not crawlable" which lists each failing anchor element with its HTML.

The Fix

1. Always Include a Valid href

Every navigation link needs a real URL in the href attribute.

<!-- Before -->
<a onclick="navigate('/about')">About</a>

<!-- After -->
<a href="/about">About</a>

If you need JavaScript behavior, use the href as the fallback:

<a href="/about" onclick="handleClick(event)">About</a>

2. Replace javascript:void(0)

This pattern was common before we had better alternatives. Replace it with a real URL.

<!-- Before -->
<a href="javascript:void(0)" onclick="showModal()">Open Modal</a>

<!-- After: if it's a true navigation -->
<a href="/modal-content">Open Modal</a>

<!-- Or: if it's an action, use a button -->
<button type="button" onclick="showModal()">Open Modal</button>

Rule of thumb: If clicking takes you to a new page, use <a> with an href. If it triggers an action on the current page, use <button>.

Framework routers should handle this automatically, but verify your implementation:

<!-- Vue Router -->
<router-link to="/about">About</router-link>
<!-- Renders: <a href="/about">About</a> ✓ -->

<!-- React Router -->
<Link to="/about">About</Link>
<!-- Renders: <a href="/about">About</a> ✓ -->

If you're using custom click handlers instead of router components, switch to the proper router link component.

4. Handle Dynamic Navigation

When URLs are generated dynamically, ensure the href is populated at render time—not just on click.

<!-- Before: href is empty until clicked -->
<a href="" @click="href = getProductUrl()">View Product</a>

<!-- After: href is set during render -->
<a :href="productUrl">View Product</a>
// Compute the URL upfront
const productUrl = computed(() => `/products/${product.id}`)

5. Fix Anchor Placeholders

If you're using <a> without href as a placeholder or for styling, reconsider.

<!-- Before: anchor as placeholder -->
<a class="nav-item disabled">Coming Soon</a>

<!-- After: use span for non-links -->
<span class="nav-item disabled">Coming Soon</span>

The only valid case for <a> without href is as a named anchor: <a id="section-1"></a>. Even then, prefer id on any element.

6. Escape Hatch: ARIA Role

If you must use an anchor without href for technical reasons, add role to exclude it from the audit:

<a role="button" onclick="showMenu()">Menu</a>

But this is a band-aid. Prefer a real <button> element.

Framework-Specific Solutions

Next.js — Always use the Link component from next/link, never raw anchors with click handlers. The Link component renders a proper <a> with href for crawlers.
Nuxt — Use NuxtLink for all internal navigation. It handles both client-side routing and proper href rendering. Avoid @click navigation on raw anchors.

Verify the Fix

  1. Run Lighthouse SEO audit again
  2. Confirm "Links are crawlable" shows as passing
  3. Use "View Page Source" (not DevTools) to see what Googlebot sees before JavaScript runs
  4. Check Google Search Console for crawl errors on pages that were previously unreachable

Common Mistakes

  • Using anchors for buttons — If it doesn't navigate to a URL, it shouldn't be an <a>. Use <button> for actions like modals, dropdowns, and form submissions.
  • Empty href with hash routing<a href="" @click="$router.push('/about')"> still fails the audit. Use <router-link> or include the href.
  • Forgetting SSR rendering — In SSR apps, check that hrefs are populated during server render, not just after hydration.
  • File protocol linksfile:/// URLs are flagged as uncrawlable because they're not accessible to search engines. Upload files to your server and use http/https URLs.

Crawlable anchor issues often appear alongside:

  • Link Text — Links need both valid hrefs and descriptive text
  • Link Name — Accessibility also requires links to have names
  • Canonical — Links should point to canonical URLs

Test Your Entire Site

Uncrawlable links often hide in navigation components, footers, or dynamically loaded sections. Unlighthouse scans your entire site and identifies every page with uncrawlable anchors, so you can fix them all at once.