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
1. Links render after JS hydration
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
useEffectfetch) - 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.
2. Links live inside collapsed accordions / drawers / tabs
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
4. Link is image onclick / button + JS navigation
<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.
5. Link inside noscript or iframe
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
Step 1: Check if your internal links survive without JS
# 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.
Step 2: Move critical internal links to SSR / SSG
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.
Step 3: Expand critical links out of collapsed / Tab containers
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?
Related
Tags: #SEO #Google #Search Console #Indexing