Internal Links Not Discovered by Google: Diagnose and Fix

Pages exist and you've linked to them, but Search Console shows 0 internal links. Rule out the May-June 2026 GSC bug, then fix JS-rendered links, nofollow, and pseudo-links.

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

Fastest answer: before you change any code, rule out two things. (1) Between May 21 and June 12, 2026 the Search Console Links report itself was broken — many sites saw zero links or 87-90% drops overnight, and even the interim “fix” only rolled the numbers back to stale data. If your drop is recent and site-wide, it may have been the report, not your links. (2) The report is a sample, not a complete index, and it lags by days. So a single URL showing “0” can be normal. If the zero persists for weeks and is specific to certain pages, then it’s almost always the real problem below: Googlebot can’t see those links. It doesn’t execute every script on the first pass, doesn’t click, and doesn’t expand accordions. Links visible in your browser may simply not exist in Google’s fetched HTML.

First, rule out the report itself

The Links report is not a live, exhaustive count:

  • It’s a sample. Google’s own docs say it “isn’t a comprehensive list of every link on your site… It shows a sample of internal and external links.” A high-volume page may genuinely have links Google followed that never surface here.
  • It’s delayed. Link data is days behind crawling and is decoupled from ranking systems. A page you linked yesterday won’t show up today.
  • It broke in 2026. The Links report stopped reporting fresh data on May 21, 2026. Around May 23 Google rolled it back to roughly week-old numbers (so it looked fixed but showed stale counts), and fresh counts only fully returned on June 12, 2026. If your “0 links” or sudden collapse lines up with that window, wait for fresh data before touching code.

Even when the report under-counts, Google may still be using the links algorithmically — the report is a diagnostic, not the ranking signal. Confirm a specific link with URL Inspection (below) rather than trusting the aggregate count.

Which bucket are you in?

SymptomLikely causeConfirm with
Site-wide collapse around late May 2026The GSC report bug (now fixed)Compare dates; re-check after June 12
Specific pages show 0; links only appear after page interactionJS-rendered linkscurl static HTML vs browser count
Links sit in collapsed accordions / Tab panelsHidden-link discountSearch templates for display: none, <details>, hidden tabpanels
Link count low despite many links in sourceAccidental rel="nofollow"grep the rendered HTML for nofollow
Cards/buttons navigate but don’t countNot real <a href> (onclick / data-link)View source; look for <button>/<div> with JS nav
Target never crawled at allTarget blocked by robots.txtCheck robots.txt + URL Inspection coverage

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 does render JS, but it happens in a second wave. Googlebot first indexes the raw HTML, then queues the page for the Web Rendering Service (WRS), which runs headless Chromium and executes the JS later. As of June 2026:

  • The median render delay is about 5 seconds, but that median hides a long tail. Onely’s testing has found 5-50% of new JS-dependent pages still unindexed two weeks after submission.
  • WRS doesn’t wait indefinitely for async work (e.g. a list rendered only after a useEffect fetch resolves).
  • Under crawl-budget pressure or rendering-queue load, the second wave can lag days to weeks, so JS-only links are discovered slowly or not at all.

How to confirm:

# What Google sees on the first pass = the static HTML response
curl -sL https://yourdomain.com/page | grep -c "href=\"/article-"
# Compare to the count you see in the browser (DevTools → Elements → Cmd+F "href=")

Or use Search Console → URL Inspection → “Test live URL” → “View tested page” → “HTML” and search for your link. If it’s missing from the crawled HTML, JS is hiding it.

Even with statically generated HTML, links inside display: none, a default-collapsed <details>, or a hidden Tab panel are discounted. The crawler can read the HTML, but Google has long said it weights hidden content less than visible content, and links you rely on for discovery shouldn’t depend on a click.

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

3. Accidental nofollow

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

Since 2020 Google treats rel="nofollow" as a hint rather than a strict directive, so it may still crawl the target — but it’s not guaranteed to, and a nofollowed internal link wastes the internal PageRank you’d otherwise pass to your own page. Internal links should never be nofollowed. Common sources of accidental nofollow:

  • WYSIWYG editor default adds nofollow (many CMSes)
  • UGC module auto-nofollows everything in the block
  • An outbound-link template copy-pasted into internal links

How to confirm:

curl -sL https://yourdomain.com/page | grep -E 'nofollow|sponsored|ugc'
# Any output = an attribute is present; inspect which links it's on
<button onclick="location.href='/article'">View article</button>
<div data-link="/article" class="card"></div>

These aren’t <a href> elements. Google has confirmed it only follows real href links in <a> tags — onclick, data-link, and router-only navigation don’t count as links for discovery.

Content inside <noscript> is generally ignored, and links inside an <iframe> belong to the framed document — they don’t pass discovery or authority to the parent page.

6. Target URL blocked by robots.txt

The link is visible, but the target URL is Disallowed in robots.txt, so Google won’t crawl it. URL Inspection will report “Blocked by robots.txt” for the target.

Shortest path to fix

# Pull the static HTML response (what the first crawl pass sees)
curl -sL https://yourdomain.com/your-page > raw.html

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

# Compare to the browser count (DevTools → Elements → Cmd+F "href=")

A big delta means most links are JS-rendered and discovery is unreliable.

In Next.js, render the links on the server, not in a client useEffect:

// Wrong: useEffect fetch, then render — links exist only after hydration
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 — links are in the HTML response
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>);
}

In the App Router, keep the component a Server Component (no "use client") so the markup ships in the HTML. Astro is SSG by default — no issue. Vue: use Nuxt useAsyncData. Svelte: use a SvelteKit load function.

Switch the “related articles” module from <details> to a default-open <section>. Do the same for core navigation hidden inside Tab panels. If the design demands collapse, at least emit all links in the HTML and control visibility with CSS — don’t drop them from the DOM while collapsed.

Step 4: Audit rel="nofollow"

# Scan your source for nofollow on links
rg 'rel="[^"]*nofollow' src/

Internal links should not carry nofollow; reserve it (or sponsored / ugc) for the links it’s actually meant for. Fix the source: stop the CMS WYSIWYG from adding nofollow by default, and scope UGC nofollow to user-submitted content only, 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>

If you need a button’s look, style an <a> to look like one — keep the real href.

Step 6: Verify with Search Console URL Inspection

After fixes: Search Console → URL Inspection → enter the page URL → “Test live URL” → “View tested page” → “HTML” and Cmd+F your target internal link. If it’s in the crawled HTML, Google can see it. Then click “Request indexing” on the target page to nudge re-crawl.

How to confirm it’s fixed

  1. Crawled HTML contains the link (URL Inspection, live test) — the definitive check, available immediately.
  2. The target’s coverage moves from “Discovered – currently not indexed” toward “Crawled” then “Indexed.”
  3. The Internal links → Top linked pages count rises. This is the slowest signal: it’s a delayed sample, so allow 1-2 weeks (longer if you’re checking during a report outage like the May-June 2026 one). Don’t treat a still-zero count here as proof the fix failed if checks 1 and 2 already passed.

Prevention

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

FAQ

Search Console suddenly shows 0 internal links across my whole site. Did I break something? Probably not, if it happened in late May 2026. The Links report broke on May 21, 2026 and only returned fresh data on June 12, 2026. Check whether your drop matches that window before changing code. Outside that window, a site-wide zero is unusual and worth investigating.

Does Google really not render JavaScript? It does, but in a deferred second wave via the WRS. The median delay is around 5 seconds, but a meaningful tail of pages waits days to weeks, and 5-50% of JS-dependent pages can stay unindexed two weeks after submission. For links you depend on for discovery, ship them in the server HTML.

Are nofollow internal links ignored completely? Since 2020 nofollow is a hint, so Google may still crawl the target — but it’s not guaranteed, and you waste the internal authority you’d otherwise pass. Never nofollow your own internal links.

Why is the count in Search Console lower than the number of links I actually have? The Links report is an explicit sample, not an exhaustive list, and it lags by days. A lower number doesn’t mean Google missed links; confirm individual links with URL Inspection instead.

My links are real <a href> and server-rendered, but the target still says “Discovered – currently not indexed.” Why? Discovery worked; indexing is a separate decision. That status usually means crawl-budget or quality reasons, not a link problem. Strengthen internal linking from already-indexed pages and request indexing on the target.

How long until the Internal links report reflects my fix? Typically 1-2 weeks, because it’s a delayed sample. Don’t wait on it to validate the fix — use URL Inspection’s crawled HTML, which updates immediately.

External references: Google: Links report, Google: Fix Search-related JavaScript problems, Google: Qualify outbound links (nofollow / sponsored / ugc).

Tags: #SEO #Google #Search Console #Indexing