TL;DR
There are two versions of every page: the one your server sends and the one that exists after JavaScript has run. Your element inspector shows you the second, which is why it will happily show you a canonical tag that was never in the HTML Google first read. View the raw source instead.
There are two versions of every page: the one your server sends, and the one that exists after JavaScript has run. Google reads both, but not at the same time and not with the same certainty, and almost every rendering problem I find lives in the gap between them.
The practical version of this: view the raw source, not the inspector. Your browser’s element inspector shows you the rendered DOM, the page after everything has executed. It will happily show you a canonical tag, a meta description or an H1 that was not in what the server sent.
The gap is measurable
HTTP Archive’s 2025 Web Almanac found a canonical tag in the raw HTML of 64.3% of mobile pages and in the rendered HTML of 66.1%.
That difference, call it two pages in a hundred, is sites where a directive Google uses to decide which URL to index does not exist until a script puts it there. It usually works. Google renders. But you have taken something that could have been a static line in the <head> and made it conditional on a script executing correctly, on a crawler’s schedule, with a rendering budget attached.
When it fails there is no error anywhere. You get a page canonicalising to nothing and you find out months later.
Why the second pass matters
Google crawls the HTML first, and renders later. Sometimes seconds later, sometimes considerably longer, depending on how much it wants your pages. Anything only present after rendering is discovered on that second visit, if it happens.
Which produces a specific and common symptom: the page is indexed but thin. Google took the raw HTML, found a shell, and indexed that. The content arrived afterwards and may or may not have been picked up. The page ranks for nothing in particular and the owner cannot work out why, because when they look at it, it is full of content.
The same applies to links. A link that only exists after JavaScript runs is a link Google may not follow on the first pass. If your entire navigation is client-side rendered, your internal linking is conditional.
Headings behave the same way, and that is the version I find most often. A page whose heading structure is assembled by a component can have a perfect outline in the inspector and nothing but a shell in what the server sent. The audit finds no H1, the owner opens the page and sees one, and everybody concludes the tool is broken.
The scale of what has to be fetched
The Almanac’s page weight data, from 16.2 million sites, puts the median mobile home page at 632 KB of JavaScript against 22 KB of HTML. Twenty-eight times more script than document.
That is the modern web and it is not automatically a fault. But it explains the crawl stats I keep finding: sites where the majority of Googlebot’s requests are scripts and stylesheets rather than pages. Google is not refusing to index the content so much as never getting a clear run at it.
Checking yours, in about five minutes
view-source: and Ctrl+F. Look for your H1, your canonical, your main body copy and your primary navigation links. If they are not in what the server sent, they are conditional.
URL Inspection in Search Console, then “View crawled page”. This is Google telling you what it actually got. It is the closest thing to ground truth available and it is free.
Disable JavaScript and reload. Crude, fast, and it shows you the shape of the shell.
Compare word counts. Raw versus rendered. A large gap tells you where the content is coming from.
What I would actually change
Not “stop using JavaScript.” That is not advice, it is nostalgia.
- Put the directives in the served HTML. Canonical, meta robots, title, hreflang. These are small, static, and there is no good reason for them to be assembled at runtime.
- Serve the primary content and the main navigation server-side if the framework allows it. Most modern ones do, and it is a configuration decision rather than a rewrite.
- Leave the interactive parts client-side. Filters, carousels, personalisation, the cart. Nobody needs those in the first response.
The line I would draw: if Google needs it to decide what the page is, send it. If a person needs it to use the page, render it.
The short version
- Two versions of every page. The inspector shows you the wrong one.
- Two pages in a hundred have a canonical only after rendering. It works until it doesn’t, silently.
- Indexed but thin is the classic symptom. Google indexed the shell.
- Client-side navigation means conditional internal linking.
- Check
view-source:and URL Inspection. Five minutes. - Directives in the served HTML. Interactivity client-side.
If a site should be ranking and it isn’t, that’s the work I do. Technical SEO covers crawling, rendering and indexing. If you’re not sure which of several plausible problems is costing you, that’s what an SEO audit is for.

