[DON’T HAVE TIME? READ THIS]
What changed with Google’s JavaScript canonical guidance?
Google updated its JavaScript SEO documentation on December 17, 2025, addressing a timing issue specific to JavaScript-rendered sites:
- Canonicalization happens twice – Once when Google crawls raw HTML, again after rendering JavaScript
- Conflicting signals cause problems – If raw HTML has one canonical and JavaScript sets another, Google gets confused
- Two acceptable approaches: Match canonical in HTML and JS, OR only set it via JavaScript (skip HTML entirely)
- Preferred method: Set canonical in raw HTML to match what JavaScript will render
- Key warning: Don’t use JavaScript to change canonical to something different than original HTML
Why this matters: The gap between crawling raw HTML and rendering JavaScript creates opportunity for canonical signals to diverge. React, Vue, and Angular sites need to check their canonical implementation.
Bottom line: Coordinate your server-side and client-side canonical tags so Google sees consistent signals at both processing stages.
Google quietly updated its JavaScript SEO docs with new guidance that fixes a problem I’ve seen mess up indexing on JS-heavy sites.
The issue is subtle but breaks canonicalization if you’re not careful.
The Two-Phase Canonical Problem
Here’s what happens when Google processes JavaScript sites.
Googlebot crawls your raw HTML first. It reads any canonical tags in that initial response.
Then it queues your page for rendering through the Web Rendering Service. This executes your JavaScript and generates the final DOM.
If your canonical tag changes between these two phases, Google receives conflicting signals.
Your raw HTML says “this is the canonical version” and your rendered JavaScript says “actually, this other URL is canonical.”
Google has to pick one. Sometimes it ignores what you intended.
The updated documentation now explicitly addresses this timing gap.
What Google Recommends
Google gives two acceptable implementation patterns.
Option 1: Match HTML and JavaScript (Preferred)
Set the canonical URL in your server’s HTML response. Make sure your JavaScript either doesn’t touch it or sets it to the exact same value.
This gives Google consistent signals before and after rendering.
Example – your server sends:
xml
<link rel=”canonical” href=”https://example.com/product/blue-shoes” />
Your JavaScript either leaves it alone or sets it to the same URL. No conflict.
Option 2: JavaScript Only
If you must set canonical via JavaScript, leave the canonical tag completely out of your initial HTML.
Let JavaScript add it during rendering.
This avoids the conflict by ensuring Google only sees one canonical signal – the one after rendering.
Google notes this works but clearly states that setting canonical in HTML is the better approach.
What Not To Do
The docs explicitly warn against using JavaScript to change the canonical URL to something different than what’s in your original HTML.
Don’t do this:
HTML sends:
xml
<link rel=”canonical” href=”https://example.com/product/shoes” />
JavaScript changes it to:
xml
<link rel=”canonical” href=”https://example.com/product/blue-shoes” />
This creates exactly the conflict Google struggles with.
Also avoid having multiple canonical tags on the same page after rendering. Only one should exist.
Why This Breaks Indexing
When canonical signals conflict between raw HTML and rendered output, several things can go wrong.
Google might index the wrong version of your page. You wanted /product/blue-shoes indexed but Google picked /product/shoes instead.
Your consolidation signals get ignored. You’re trying to tell Google which URL is authoritative but sending mixed messages weakens that signal.
Pages might not get indexed at all. If Google can’t determine the canonical version confidently, it might skip indexing the page.
I’ve debugged this exact issue on React sites where client-side routing was dynamically setting canonicals that conflicted with server-rendered HTML.
How To Check Your Implementation
Use Google Search Console’s URL Inspection tool to compare raw HTML and rendered output.
Enter your URL and run the live test. The tool shows you both versions.
Look for:
- User-declared canonical – What you specified in your code
- Google-selected canonical – What Google actually chose to index
If these don’t match, you have a canonicalization problem.
The URL Inspection tool also shows the raw HTML response and the rendered HTML after JavaScript executes. Compare canonical tags in both.
[IMAGE: Screenshot showing URL Inspection tool with raw HTML vs rendered HTML canonical comparison]
Framework-Specific Considerations
If you’re using React, Vue, Angular, or Next.js, check how your framework handles canonical tags.
React/Vue/Angular (Client-Side Rendering)
These frameworks often inject canonical tags via JavaScript after initial page load.
Your server sends minimal HTML. JavaScript builds the DOM and adds canonical tags.
This matches Google’s Option 2 – skip canonical in initial HTML, let JavaScript handle it entirely.
But make sure you’re actually setting canonicals. I’ve audited sites where developers assumed the framework handled it but no canonical tags existed anywhere.
Next.js/Nuxt (Server-Side Rendering/Static Generation)
These frameworks render HTML on the server, so your canonical should be in the initial response.
Your JavaScript hydration shouldn’t modify the canonical tag.
This matches Google’s Option 1 – set in HTML, leave it alone or match it in JavaScript.
Check your <Head> component to ensure canonicals are set server-side.
Gatsby/Hugo (Static Site Generators)
These generate static HTML at build time.
Your canonical tags should be in the HTML files before any JavaScript runs.
Generally safer from canonicalization conflicts since there’s no dynamic rendering changing things.
When JavaScript Canonical Changes Make Sense
There are legitimate cases where you need JavaScript to set canonical URLs.
Single-page applications (SPAs) where URLs change without page reloads. Your canonical needs to update dynamically.
Progressive web apps (PWAs) where content is entirely client-rendered.
In these cases, follow Google’s guidance: either match HTML and JavaScript exactly, or skip HTML canonical entirely.
But understand the tradeoff. Google has to wait for rendering to see your canonical signal. This delays canonicalization compared to having it in raw HTML.
Implementation Checklist
Here’s what to audit on your JavaScript site:
- Check if canonical tags exist in your server’s HTML response (view source in browser, don’t use inspect element)
- Verify your JavaScript isn’t modifying existing canonical tags to different values
- Test a few URLs with URL Inspection tool to compare raw vs rendered canonicals
- Ensure only one canonical tag exists per page after rendering
- Use absolute URLs in canonical tags, not relative paths
- Validate canonical URLs actually exist and return 200 status codes
For React/Vue/Angular sites, check your routing setup. Make sure canonical tags update correctly when routes change.
For server-rendered frameworks (Next.js, Nuxt), verify canonical tags are in initial HTML before hydration runs.
What Google Didn’t Change
This documentation update clarifies existing behavior. It doesn’t change how Google processes canonical tags.
If you’ve been following canonical best practices – using absolute URLs, setting one canonical per page, making canonicals consistent – you’re fine.
The update specifically helps JavaScript site developers understand the two-phase processing and avoid conflicts between stages.
If you’re seeing unexpected canonical selection in Search Console’s Page Indexing report, this timing mismatch might be the cause.
The Simple Fix
For most sites, the fix is straightforward.
Set your canonical URL in the server’s HTML response. Make it match the URL you want indexed.
Don’t touch it with JavaScript. Or if you must, set it to the exact same value.
This keeps your signals consistent across both phases of Google’s processing.
Test with URL Inspection tool to confirm Google sees the same canonical before and after rendering.
That’s it. Simple problem, simple fix, but easy to miss if you don’t understand Google’s two-phase crawling for JavaScript sites.
