Internal Links Not Discovered by Google

Pages exist but Google doesn't see your internal links pointing to them.

Your page clearly has 50 internal links pointing to various articles, but Search Console → Links → Internal links shows certain URLs with “0 internal links.” Or URL Inspection shows “Discovered – not indexed” even though you’ve linked to it from 5 pages.

The most common cause isn’t that Google refuses to count — it can’t see those links at all. Googlebot doesn’t execute JS by default, doesn’t click, doesn’t expand accordions. Links visible in your browser may simply not exist in Google’s fetched snapshot.

Common causes

React / Vue / Svelte client-side rendered links don’t exist in the initial HTML; JS mounts the component and they appear later.

Google’s Web Rendering Service does run JS, but:

  • WRS doesn’t wait for async loads (e.g., lists rendered after useEffect fetch)
  • WRS has resource limits, complex SPAs may be skipped
  • Initial crawl often judges from the static HTML snapshot alone

How to confirm:

curl -sL https://yourdomain.com/page | grep -c "href=\"/article-"
# Compare to the count you see in the browser

Or use Search Console → URL Inspection → “Test live URL” → “Screenshot + View crawled HTML”: search for your link, missing = JS hides it.

Even if HTML is statically generated, links inside display: none or default-collapsed <details> or hidden Tab panels are usually discounted (the crawler technically sees the HTML, but signal weight drops).

How to confirm: Search your templates for display: none, hidden, <details>, tabpanel hidden.

3. Accidental nofollow

<a href="/article" rel="nofollow">Link</a>

rel="nofollow" tells Google “don’t follow and don’t pass authority.” Common sources:

  • WYSIWYG editor default adds nofollow (many CMSes)
  • UGC module auto-nofollows
  • Outbound link template copy-pasted into internal links

How to confirm:

curl -sL https://yourdomain.com/page | grep -E 'nofollow|sponsored|ugc'
# Any output = it's there
<button onclick="location.href='/article'">View article</button>
<div data-link="/article" class="card"></div>

These aren’t <a href> — for Google, the link doesn’t exist.

Most <noscript> content is ignored; iframe links don’t pass authority to the parent page.

6. Target URL blocked by robots.txt

The link is visible, but the target URL is Disallowed — Google refuses to follow.

Shortest path to fix

# Pull static HTML
curl -sL https://yourdomain.com/your-page > raw.html

# Count <a href>
grep -oE '<a [^>]*href="[^"]+"' raw.html | wc -l

# Compare to what your browser shows (DevTools → Elements → Cmd+F search href= count)

Big delta = most links are JS-rendered, Google can’t see them.

In Next.js:

// Wrong: useEffect fetch, then render
function Related() {
  const [items, setItems] = useState([]);
  useEffect(() => { fetch('/api/related').then(r => r.json()).then(setItems); }, []);
  return items.map(i => <a href={i.url}>{i.title}</a>);
}

// Right: getStaticProps / getServerSideProps
export async function getStaticProps() {
  const items = await getRelated();
  return { props: { items } };
}
function Related({ items }) {
  return items.map(i => <a href={i.url}>{i.title}</a>);
}

Astro is SSG by default — no issue. Vue: use Nuxt async data. Svelte: use SvelteKit load.

Switch the “related articles” module from <details> to a default-open <section>. Same for core nav inside Tab panels.

If the design demands collapse, at least emit all links in HTML and control visibility with CSS — don’t omit them from the DOM while collapsed.

Step 4: Audit rel="nofollow"

# Site-wide scan
rg 'rel="[^"]*nofollow' src/

# Internal links should not have nofollow; external ones as needed

Fix: CMS WYSIWYG default → stop adding nofollow; UGC modules should only nofollow user-submitted content, not template-defined related links.

Step 5: Switch every clickable element to <a href>

<!-- Wrong -->
<button onclick="location.href='/article'">View</button>
<div data-link="/article" class="card">...</div>

<!-- Right -->
<a href="/article" class="button">View</a>
<a href="/article" class="card">...</a>

Step 6: Verify with Search Console URL Inspection

After fixes, Search Console → URL Inspection → enter the page URL → “Test live URL” → “View crawled HTML.” Cmd+F your target internal link — found = Google sees it.

Wait 1-2 weeks for the Internal links report to show the link count rising.

Prevention

  • Critical internal links always SSR / SSG — never client-side fetch only
  • Use real <a href> — no onclick / data-href pseudo-links
  • CI runs a disabled-JS spot check: curl static HTML, assert critical internal link count ≥ X
  • nofollow only on sponsored external links / UGC; templates never default-add it
  • Collapse / Tab designs go through SEO review: can core nav default-expand?

Tags: #SEO #Google #Search Console #Indexing