You migrated /blog/old-post/ to /blog/new-post/, set the new page’s canonical to self-referencing, and waited. Three weeks later, Google still returns the old URL. URL Inspection on the new page shows: Indexed, though Google selected a different canonical than user, pointing at /blog/old-post/. You changed the tag. Why did nothing happen?
Fastest fix: add a 301 redirect from the old URL to the new one, then update every internal link and your sitemap so they reference the new URL. The canonical tag by itself almost never wins, because <link rel="canonical"> is a hint, not a directive. Google calls it a “strong signal” in its canonicalization docs, but it weighs it against redirects, internal links, sitemap entries, backlinks, and content — and a self-canonical on a page that otherwise looks like the duplicate loses that vote. As of June 2026, Google’s own canonicalization troubleshooting guide is blunt about it: “Even if you explicitly designate a canonical page, Google might choose a different canonical for various reasons, such as the quality of the content.” The job is not to “change the canonical harder.” It’s to make every other signal agree with it.
Which case are you in?
Run these three checks first. Most failures map to exactly one row, and the fix differs per row.
| Check | Command | If you see this | You’re in |
|---|---|---|---|
| Old URL still serves | curl -sI .../blog/old-post/ | head -1 | HTTP/2 200 | Case 1 (no 301) |
| Internal links | rg -l '/blog/old-post' src/ | any files listed | Case 2 |
| Sitemap | curl -s .../sitemap.xml | grep old-post | old URL present | Case 3 |
| Self-canonical mismatch | curl -s .../blog/new-post/ | grep canonical | href is http://, www., or a different slash than the live URL | Case 4 |
If all four checks are clean, you’re in Case 5 (backlinks), Case 6 (thin new page), or Case 7 (Google is just slow). Work top to bottom — a missing 301 makes everything else moot.
Case 1: No 301 from old URL to new URL
How to spot it:
curl -sI "https://yourdomain.com/blog/old-post/" | head -1
# HTTP/2 200 <-- old URL still serving 200, no redirect
The old URL still resolves directly. Both pages are live, both return 200, and the only difference is the canonical tag on the new one.
Why it happens: most “URL change” workflows only edit the canonical and leave the old route serving. Google sees two live pages with near-identical content. One has accumulated indexing history, internal links, and backlinks; the other has only a canonical tag. The old page wins.
Fix: add a 301 from old to new. A redirect and a canonical are both “strong signals” in Google’s own ranking of methods, but the redirect wins decisively for one reason — after it ships, the old URL stops existing as a destination, so there is no longer a competing live page for Google to prefer. Google rarely ignores a 301.
// Vercel: next.config.js
module.exports = {
async redirects() {
return [{ source: "/blog/old-post", destination: "/blog/new-post", permanent: true }];
},
};
// Firebase Hosting: firebase.json
{
"hosting": {
"redirects": [
{ "source": "/blog/old-post", "destination": "/blog/new-post", "type": 301 }
]
}
}
After deploying, verify:
curl -sIL "https://yourdomain.com/blog/old-post/" | grep -iE "^HTTP|^location"
# HTTP/2 301
# location: https://yourdomain.com/blog/new-post/
# HTTP/2 200 <-- final hop, the new page
Confirm it’s a single 301 hop landing on a 200, not a chain (301 -> 301 -> 200) or a redirect to a 404. Multi-hop chains and redirects to dead pages both weaken consolidation.
Case 2: Internal links still point to the old URL
How to spot it — search your codebase:
rg -l '/blog/old-post' src/
# Returns the list of files still referencing the old URL
Sitemap, related-posts widget, breadcrumbs, in-article links, navigation — any of these still pointing at the old URL adds weight to “the old URL is the real one.” Google explicitly lists internal links that predominantly point at the non-canonical version as a reason it overrides your declared canonical.
Why it happens: migrations rarely catch every reference. CMS rich-text fields, hand-written Markdown, generated taxonomies — links sneak through.
Fix — bulk-replace:
# Find every reference
rg -l '/blog/old-post' src/
# Replace (macOS sed)
rg -l '/blog/old-post' src/ | xargs sed -i '' 's|/blog/old-post|/blog/new-post|g'
# On Linux, drop the empty '' after -i:
# rg -l '/blog/old-post' src/ | xargs sed -i 's|/blog/old-post|/blog/new-post|g'
# Verify
rg '/blog/old-post' src/
# (empty)
Then in Search Console, open the Links report from the left sidebar and check Internal links. The new URL’s link count should rise within a crawl cycle.
Case 3: Sitemap still lists the old URL
How to spot it:
curl -s https://yourdomain.com/sitemap.xml | grep old-post
# Shows the old URL still in the sitemap
A sitemap is the one place you tell Google directly which URL you intend to be indexed. Google rates sitemap inclusion as a “weak signal” on its own, but when sitemap says A and canonical says B, that conflict still tugs the decision toward A.
Why it happens: sitemap regeneration didn’t pick up the change because the migration was a one-off rename, not a rebuild with a fresh sitemap.
Fix: regenerate the sitemap so the new URL appears and the old one is gone. Then in Search Console, open Sitemaps (under Indexing in the left sidebar) and resubmit the sitemap URL.
Case 4: The new page’s self-canonical doesn’t match its own live URL
How to spot it:
curl -s "https://yourdomain.com/blog/new-post/" | grep -i canonical
# <link rel="canonical" href="http://yourdomain.com/blog/new-post"/>
# ^^^^ http not https, and no trailing slash
The new page declares a canonical, but it points at a slightly different string than the URL Google actually crawled: http vs https, www. vs bare domain, or a trailing slash that’s present in the live URL but absent in the tag (or vice versa). Google treats those as different URLs, so your “self”-canonical isn’t actually self-referencing — it points at a URL variant that may not even resolve. This is one of the most common silent causes; Google’s troubleshooting guide lists “incorrect rel=canonical markup” among the top reasons it picks a different canonical, and inconsistent HTTP/HTTPS, www, and trailing-slash forms are exactly that.
Why it happens: the canonical is hardcoded or built from a different base URL than the one your host serves (e.g. the template hardcodes http://, or your CDN forces a trailing slash that the canonical helper omits).
Fix: make the canonical href byte-for-byte identical to the live, post-redirect URL — same scheme, same host, same trailing-slash convention. Pick one canonical form for the whole site (https + bare-or-www + consistent slash) and enforce it in both your redirects and your canonical helper, so they can never drift apart.
# Confirm the canonical href matches the final URL after redirects
FINAL=$(curl -s -o /dev/null -w '%{url_effective}' -L "https://yourdomain.com/blog/new-post/")
CANON=$(curl -sL "https://yourdomain.com/blog/new-post/" | grep -oiE 'rel="canonical" href="[^"]+"')
echo "final: $FINAL"
echo "canon: $CANON"
# The href inside $CANON must equal $FINAL exactly.
Case 5: Old URL has external backlinks the new URL lacks
How to spot it: in Search Console, open the Links report and compare External links for the two URLs. If the old URL has 50 referring domains and the new one has 3, Google heavily weights the old URL’s authority.
Why it happens: backlinks accumulate over years. A renamed URL starts at zero. Even with every internal signal pointing at the new URL, external links keep the old one’s authority alive.
Fix: the 301 from Case 1 transfers the great majority of that backlink equity to the new URL. There is no faster route. For the handful of high-value backlinks that matter most, email those sites to update the link directly.
Case 6: New URL is thinner than the old one
How to spot it: open both pages side by side. Count words; compare the H1/H2 structure. If the new page is a stub, or its content differs substantially from the old page, Google may treat them as two different pages rather than a renamed version of one — and Google’s documentation names “significantly different content” as a reason it ignores your canonical.
Why it happens: someone refactored the URL during a redesign and the new template stripped sidebar content, related links, or footer info.
Fix: this is the slowest case. Either bring the new page up to match or exceed the old one’s depth, or accept that consolidation takes longer (8 to 12 weeks).
Case 7: You did everything right and Google is just slow
How to spot it: the 301 is live, internal links are migrated, the sitemap is clean, URL Inspection on the new URL shows URL is on Google — yet site:yourdomain.com/blog/old-post still returns a result.
Why it happens: canonical consolidation is not instant. After all signals point to the new URL, the old one lingers in the index for weeks while Google reconfirms the change is permanent.
Fix: wait. As of June 2026, plan on 4 to 12 weeks. If the old URL must disappear from results urgently, use the Removals tool (Indexing -> Removals -> New request -> Temporarily remove URL). It hides the URL from Search for about six months; it does not deindex it, so keep the 301 in place to do the real consolidation.
Shortest fix path
In hit-rate order:
- Add a
301from old to new — ~70% of cases. The single strongest practical move, because it removes the competing live page. - Update internal links sitewide — ~20% of cases. The
301fixes external authority; internal links fix internal authority. - Fix a mismatched self-canonical and regenerate the sitemap — ~8% of cases. Make the canonical href byte-identical to the live URL, and resolve the “site owner says X but page tag says Y” conflict.
- Wait 4 to 12 weeks — the last ~2%. With steps 1 to 3 clean, time alone resolves the remainder.
How to confirm it’s fixed
After applying the fix, check each item:
# 1. Old URL 301s to new (single hop, lands on 200)
curl -sIL "https://yourdomain.com/blog/old-post/" | grep -iE "^HTTP|^location"
# Expect: HTTP/2 301 -> location: .../blog/new-post/ -> HTTP/2 200
# 2. New URL is 200 and self-canonical
curl -s "https://yourdomain.com/blog/new-post/" | grep canonical
# Expect: <link rel="canonical" href="https://yourdomain.com/blog/new-post/">
# 3. Sitemap lists new, not old
curl -s "https://yourdomain.com/sitemap.xml" | grep blog/old-post
# Expect: (empty)
curl -s "https://yourdomain.com/sitemap.xml" | grep blog/new-post
# Expect: <loc>https://yourdomain.com/blog/new-post/</loc>
# 4. No internal links to old URL
rg "/blog/old-post" src/
# Expect: (empty)
Then run URL Inspection on the new URL in Search Console and click Request Indexing. Note the daily cap: as of June 2026, Search Console accepts only roughly 10 to 12 manual indexing requests per property per day, so prioritize the new URL rather than spamming every page. The final canonical decision is reported under the Page indexing (formerly “Coverage”) report once Google recrawls — Request Indexing speeds the crawl, not the consolidation verdict.
Prevention
- Treat a URL change as a four-step migration, never a one-step tag edit: (1) deploy the new URL, (2) add a
301from the old one, (3) update internal links sitewide, (4) regenerate the sitemap. Skip any step and the canonical change is wasted. - Generate every URL from one
urlFor(slug)helper so a rename touches only the slug map. - Avoid renaming high-traffic URLs for cosmetic reasons. The transition costs 4 to 12 weeks of degraded rankings.
- Track canonical/sitemap/internal-link consistency in CI: fail the prebuild if a sitemap URL has no matching internal link, or if a page’s canonical doesn’t match its sitemap entry.
FAQ
Q: Why not just delete the old URL with a 410?
A: 410 (Gone) tells Google to drop the URL from the index, which discards the backlink equity the old URL accumulated. A 301 transfers most of that equity to the new URL. Use 301 unless the page is genuinely gone with no replacement.
Q: How long should I wait before assuming the canonical change failed?
A: 4 weeks is the minimum for measurable movement; 8 weeks is typical for a full transition. If after 12 weeks Google still shows the old URL while every signal (301, internal links, sitemap, content) points at the new one, something is broken — usually a multi-hop or dead-ending 301 chain, or a CDN cache serving the old page intermittently. Re-run the Case 1 curl -sIL check.
Q: Can I speed up the re-crawl?
A: Run URL Inspection -> Request Indexing on the new URL (and the old one if you want it recrawled to see the 301). That usually triggers a fetch within hours, but you’re limited to roughly 10 to 12 requests per property per day. Re-crawling is fast; re-evaluating the canonical still takes weeks, and no tool forces that timeline.
Q: I changed the canonical but didn’t add a 301. Will Google ever switch?
A: Possibly, but rarely. Without a 301, both pages stay live and Google has no strong reason to consolidate. If the new page later accumulates more internal links, Google may flip — but the canonical tag alone is too weak against an established duplicate. Add the 301.
Q: URL Inspection says “Indexed, though Google selected a different canonical than user.” Is my page still indexed? A: Yes — the page is in Google’s index, but ranking signals (PageRank, backlinks, and so on) are credited to the canonical Google chose, not the one you declared. Searchers usually land on Google’s pick, not yours. This status is not a penalty, just a consolidation decision you can override by aligning the signals above. See Duplicate, Google chose different canonical for the full diagnostic flow.
Q: My canonical and live URL differ only by a trailing slash (or http vs https, or www). Does that matter?
A: Yes, a lot. Google treats https://site.com/page, https://site.com/page/, http://site.com/page, and https://www.site.com/page as four distinct URLs. If your self-canonical points at a different variant than the one Google crawled, the page is effectively canonicalizing to something else — sometimes a URL that 301s away or doesn’t resolve. Make the href byte-for-byte identical to the final, post-redirect URL (see Case 4) and standardize on one form sitewide.
Q: Does a noindex or robots.txt block on the old URL help it consolidate faster?
A: No — it backfires. If the old URL is blocked by robots.txt, Google can’t crawl it to see the 301, so it never learns where to send the equity. A noindex on the old page deindexes it without transferring authority. Leave the old URL crawlable and let the 301 do the work.
Related articles
- Canonical misconfigured: 3 failure modes
- Duplicate, Google chose different canonical than user
- Canonical still points to old domain after migration
- Alternate page with proper canonical tag
- Pagination canonical confusion
Tags: #SEO #Troubleshooting #Debug #Structured data #Canonical