改了 canonical,但 Google 仍显示旧 URL

你把 canonical 改到了新 URL,几周后 Google 还是排名旧的。canonical 是提示不是命令——把每个信号都对齐,Google 才会切换。

你把 /blog/old-post/ 迁到了 /blog/new-post/,把新页的 canonical 改成自指,然后等。三周过去,Google 搜索结果里还是旧 URL。对新页做 URL 检查,结论是 Indexed, though Google selected a different canonical than user,指向 /blog/old-post/。标签明明改了,怎么一点效果都没有?

最快的修复:从旧 URL 加一条到新 URL 的 301 重定向,再把全站内链和 sitemap 都改成指向新 URL。只靠 canonical 标签几乎永远赢不了,因为 <link rel="canonical">提示,不是命令。Google 在规范化文档里把它叫作”强信号”,但它会和重定向、内链、sitemap 条目、外链、内容一起加权——一个页面其他方面都像”重复页”,单凭一个自指 canonical,这一票就输了。截至 2026 年 6 月,Google 自己的规范化排错指南说得很直白:“Even if you explicitly designate a canonical page, Google might choose a different canonical for various reasons, such as the quality of the content.”(即便你显式指定了规范页,Google 仍可能因内容质量等各种原因选择另一个 canonical。)要做的不是”把 canonical 改得更显眼”,而是让其他所有信号都和它保持一致。

先判断你属于哪种情况

先跑这三个检查。大多数失败都正好落在某一行上,而每一行的修法不同。

检查项命令看到这个你属于
旧 URL 仍在线curl -sI .../blog/old-post/ | head -1HTTP/2 200情况 1(没 301)
站内内链rg -l '/blog/old-post' src/列出任何文件情况 2
sitemapcurl -s .../sitemap.xml | grep old-post旧 URL 还在情况 3
自指 canonical 不一致curl -s .../blog/new-post/ | grep canonicalhref 是 http://、带 www.、或斜杠和实际 URL 不同情况 4

如果这四项都已干净,那就是情况 5(外链)、情况 6(新页太薄)或情况 7(Google 只是慢)。从上往下处理——缺了 301,后面做什么都白搭。

情况 1:没有从旧到新的 301

怎么发现:

curl -sI "https://yourdomain.com/blog/old-post/" | head -1
# HTTP/2 200  <-- 旧 URL 仍直接返回 200,没有重定向

旧 URL 依旧能直接打开。新旧两页都在线、都返回 200,唯一差别是新页上多了一个自指的 canonical 标签。

原因:大多数”换 URL”的操作只改了 canonical,旧路由仍在对外服务。Google 看到两个内容几乎相同的活页:一个已有收录历史、内链积累、外链积累;另一个除了一个 canonical 标签什么都没有。旧的赢。

修复:从旧到新加一条 301。在 Google 自己对各方法的排序里,重定向和 canonical 都属于”强信号”,但重定向之所以能稳赢,原因只有一个——它上线之后,旧 URL 作为落地页就不存在了,Google 再也没有一个竞争性的活页可以偏向。Google 几乎不会忽略 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 }
    ]
  }
}

部署完验证:

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   <-- 最后一跳,落到新页

确认是一跳 301 落到 200,而不是一条链(301 -> 301 -> 200),也不是重定向到 404。多跳链和重定向到死页都会削弱合并效果。

情况 2:站内内链还指着旧 URL

怎么发现——搜索代码库:

rg -l '/blog/old-post' src/
# 列出所有仍然引用旧 URL 的文件

sitemap、相关文章、面包屑、文中互引、导航——任何一处还指向旧 URL,都在给”旧 URL 才是真的”加权。Google 明确把”内链大多指向非规范页”列为它推翻你声明的 canonical 的原因之一。

原因:URL 迁移很难一次扫干净,CMS 富文本字段、手写 Markdown、自动生成的分类页等地方常常漏。

修复——批量替换:

# 找出所有引用
rg -l '/blog/old-post' src/

# 替换(macOS sed)
rg -l '/blog/old-post' src/ | xargs sed -i '' 's|/blog/old-post|/blog/new-post|g'

# Linux 上去掉 -i 后面的空 '':
# rg -l '/blog/old-post' src/ | xargs sed -i 's|/blog/old-post|/blog/new-post|g'

# 校验
rg '/blog/old-post' src/
# (空)

然后在 Search Console 左侧栏打开 Links 报告,查看 Internal links。新 URL 的链接数会在下个抓取周期内上升。

情况 3:sitemap 还列着旧 URL

怎么发现:

curl -s https://yourdomain.com/sitemap.xml | grep old-post
# 旧 URL 仍在 sitemap 里

sitemap 是你唯一一个直接告诉 Google “我希望哪条 URL 被收录”的地方。单看 sitemap,Google 把它算作”弱信号”,但当 sitemap 说 A、canonical 说 B 时,这个冲突仍会把判断往 A 拉。

原因:迁移是一次性手改,sitemap 重新生成那一步没触发——它不是带着新 sitemap 的重新构建。

修复:重新生成 sitemap,新 URL 必须出现、旧 URL 必须消失。然后在 Search Console 左侧栏 Indexing 下打开 Sitemaps,重新提交 sitemap 地址。

情况 4:新页的自指 canonical 和它自己的实际 URL 对不上

怎么发现:

curl -s "https://yourdomain.com/blog/new-post/" | grep -i canonical
# <link rel="canonical" href="http://yourdomain.com/blog/new-post"/>
#                              ^^^^ 是 http 不是 https,而且没有结尾斜杠

新页声明了 canonical,但它指向的字符串和 Google 实际抓取的那条 URL 略有不同:httphttps、带 www. 对裸域名、或者实际 URL 有结尾斜杠而标签里没有(或反过来)。Google 把这些当作不同的 URL,所以你的”自指”canonical 其实没有指向自己——它指向的是另一个 URL 变体,那个变体甚至可能根本打不开。这是最常见、最隐蔽的原因之一;Google 的排错指南把”rel=canonical 标记写错”列为它另选 canonical 的首要原因之一,而 HTTP/HTTPS、www、结尾斜杠形式不一致正属于此类。

原因:canonical 是写死的,或者由一个和主机实际服务不同的 base URL 拼出来的(比如模板把 http:// 写死了,或者 CDN 强制加结尾斜杠而 canonical helper 没加)。

修复:让 canonical 的 href 和经过重定向后的实际 URL 一字不差——同样的 scheme、同样的 host、同样的结尾斜杠约定。给全站定一种 canonical 形式(https + 裸域名或带 www + 统一斜杠),并在重定向和 canonical helper 两处都强制执行,让它们永远不会跑偏。

# 确认 canonical 的 href 和重定向后的最终 URL 一致
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"
# $CANON 里的 href 必须和 $FINAL 完全相等。

情况 5:旧 URL 有外链而新 URL 没有

怎么发现:在 Search Console 打开 Links 报告,对比两个 URL 的 External links。如果旧 URL 有 50 个引用域、新的只有 3 个,Google 会大幅倾向旧 URL 的权重。

原因:外链是几年积累出来的。换 URL 等于从零开始,即便内部信号已全部迁移,旧 URL 的外链权重仍把它活活撑住。

修复:情况 1 的 301 会把绝大多数外链权重转给新 URL,这是最快的办法,没有更快的。对最有价值的那几个外链,可以直接发邮件请对方更新链接。

情况 6:新页内容比旧页薄

怎么发现:两页对开浏览。数字数、对照 H1 / H2 结构。如果新页是个短桩、或者内容与旧页差异很大,Google 会把它们当成两个不同的页面,而不是同一页换了 URL——Google 文档把”内容差异显著”列为它忽略你 canonical 的原因之一。

原因:改版时有人重构了 URL,新模板砍掉了侧栏、相关链接、页脚信息等。

修复:这是最慢的一种。要么把新页内容补到不输旧页,要么接受这次合并需要更长时间(8 到 12 周)。

情况 7:你做对了所有事,Google 只是慢

怎么发现:301 已上线、内链已迁移、sitemap 已清理、对新 URL 做 URL 检查显示 URL is on Google,但 site:yourdomain.com/blog/old-post 仍然有结果。

原因:canonical 合并不是瞬时的。所有信号已指向新 URL 之后,旧 URL 仍会在索引里停留数周,等 Google 反复确认这是一个长期变更。

修复:等。截至 2026 年 6 月,按 4 到 12 周来预期。如果旧 URL 必须尽快从结果里消失,用 Removals 工具(Indexing -> Removals -> New request -> Temporarily remove URL)。它会把该 URL 从搜索里隐藏约六个月,但并不会取消收录,所以 301 要一直留着,真正的合并还是靠它完成。

最短修复路径

按命中率排序:

  1. 从旧到新加 301 —— 约 70% 的情况。最有效的一步,因为它干掉了那个竞争性的活页。
  2. 全站更新内链 —— 约 20% 的情况。301 修外部权重,内链修内部权重。
  3. 修正对不上的自指 canonical 并重新生成 sitemap —— 约 8% 的情况。让 canonical 的 href 和实际 URL 一字不差,并化解”站长说 X、页面标签说 Y”的拉扯。
  4. 等 4 到 12 周 —— 剩下约 2%。前三步做干净后,时间会替你把余下部分处理完。

怎么确认已经修好

修完后逐项核对:

# 1. 旧 URL 一跳 301 到新页(落到 200)
curl -sIL "https://yourdomain.com/blog/old-post/" | grep -iE "^HTTP|^location"
# 期望:HTTP/2 301 -> location: .../blog/new-post/ -> HTTP/2 200

# 2. 新 URL 是 200 且自指 canonical
curl -s "https://yourdomain.com/blog/new-post/" | grep canonical
# 期望:<link rel="canonical" href="https://yourdomain.com/blog/new-post/">

# 3. sitemap 列新不列旧
curl -s "https://yourdomain.com/sitemap.xml" | grep blog/old-post
# 期望:(空)
curl -s "https://yourdomain.com/sitemap.xml" | grep blog/new-post
# 期望:<loc>https://yourdomain.com/blog/new-post/</loc>

# 4. 没有指向旧 URL 的内链
rg "/blog/old-post" src/
# 期望:(空)

然后在 Search Console 对新 URL 做 URL 检查,点 Request Indexing。注意每日上限:截至 2026 年 6 月,Search Console 每个资源每天大约只接受 10 到 12 次手动编入索引请求,所以优先提交新 URL,别把每个页面都刷一遍。最终的 canonical 判定要等 Google 重抓后,在 Page indexing(原 “Coverage”)报告里看——Request Indexing 加速的是抓取,不是合并裁决。

预防建议

  • 把换 URL 当成四步迁移、而不是一步改标签:(1) 部署新 URL,(2) 从旧加 301,(3) 全站改内链,(4) 重新生成 sitemap。少一步,canonical 修改就白搭。
  • 所有 URL 从一个 urlFor(slug) helper 生成,改名只动 slug 映射。
  • 不要为了好看而改高流量 URL——过渡期会损失 4 到 12 周排名。
  • 在 CI 里跟踪 canonical / sitemap / 内链一致性:sitemap 里有的 URL 却没有内链、或者页面 canonical 与 sitemap 条目不一致,就让构建失败。

FAQ

Q:为什么不直接 410 删掉旧 URL? A:410(Gone)会告诉 Google 把这条 URL 从索引里彻底丢掉,旧 URL 积累的外链权重也跟着丢。301 能把绝大部分权重转到新 URL。除非旧页确实没有替代品,否则用 301

Q:多久后可以判定 canonical 修改失败了? A:4 周是看到变化的最低线,8 周是完整切换的常态。如果 12 周后所有信号(301、内链、sitemap、内容)都对、Google 仍显示旧 URL,那多半是哪里坏了——通常是多跳或落到死页的 301 链,或者 CDN 缓存间歇性返回旧页。重新跑一遍情况 1 的 curl -sIL 检查。

Q:能加速重抓吗? A:对新 URL(想让旧 URL 重抓以确认 301,也可以一起)做 URL 检查 -> Request Indexing,一般几小时内就会来抓,但每个资源每天上限大约 10 到 12 次。重抓很快,canonical 决策的重新评估还是周级,没有工具能强制这条时间线。

Q:只改了 canonical 没加 301,Google 会不会自己换? A:有可能,但概率很低。没 301 时两页都活着,Google 没有强信号去合并。如果新页随时间积累了更多内链,Google 可能翻盘——但面对一个已经稳固的重复页,仅靠 canonical 标签太弱。加上 301

Q:URL 检查显示 “Indexed, though Google selected a different canonical than user”,这条 URL 还在索引里吗? A:在——Google 的索引里有这一页,但排名信号(PageRank、外链等)被算到 Google 选定的那个 canonical 上,不是你声明的这条。用户搜索时落地的也是 Google 选的那条。这不是惩罚,只是一个合并决策,把上面那些信号对齐就能翻盘。完整诊断流程见 Duplicate, Google chose different canonical

Q:我的 canonical 和实际 URL 只差一个结尾斜杠(或 httphttps、或 www),这有影响吗? A:影响很大。Google 把 https://site.com/pagehttps://site.com/page/http://site.com/pagehttps://www.site.com/page 当成四个不同的 URL。如果你的自指 canonical 指向的变体和 Google 抓取的那条不一样,这个页面实际上是在把权重指向别处——有时甚至指向一个会 301 跳走、或者根本打不开的 URL。把 href 改成和重定向后的最终 URL 一字不差(见情况 4),并在全站统一成一种形式。

Q:对旧 URL 加 noindex 或 robots.txt 屏蔽,能让它更快合并吗? A:不能,反而帮倒忙。如果旧 URL 被 robots.txt 屏蔽,Google 抓不到它,也就看不到那条 301,永远学不到该把权重转到哪儿。对旧页加 noindex 会把它取消收录、却不转移权重。让旧 URL 保持可抓取,把活儿交给 301

相关阅读

标签: #SEO #排查 #排查 #结构化数据 #Canonical