所有语言版本的 canonical 都指向英文原版,等于替 Google 做了一个决定:这不是一个新页面,只是英文页的一份副本,请把它的信号合并给英文页。被合并的 URL 不会以独立身份进入索引,你在日本站、德国站、西班牙站写的内容,在搜索结果里等于不存在。修复方式只有一条——每个语言版本写指向自己的自引用 canonical。这一步做错,后面把 sitemap 拆成几份、在 robots.txt 里列多少行、提交多少次 URL,都补不回来。
我真正撞上这件事,是凌晨一点在翻一个日本站的源码。站做了差不多四个月,英文版收录稳定,德文和法文零星进了一些,只有 /ja/ 目录一片空白。第一反应是抓取预算不够,还去 robots.txt 里反复确认没有误伤。后来随手 Ctrl+U 打开某个日文页面的源码,Ctrl+F 搜 canonical。
那一行的 href 是 https://example.com/en/。
往后滚,把德文站的页面也打开,一样。西班牙站,一样。四个语言目录,每一篇文章,canonical 全都指回英文原版。这不是某个页面写错了,是模板层面的继承:建站时英文是主模板,复制出语言版本的时候,canonical 那一行跟着复制了过去,从来没人改过。
canonical 是合并声明,不是权重建议
从那天起我看这一行的角度就变了。canonical 不是一个“权重提示”,它是一个归并声明。Google 索引的单位是 URL,不是语言——它没有“这个页面是日文版”这种概念,只有一堆 URL 和它们之间的关系。你在日文页面上写 canonical 指向英文页,它读到的是:这两个 URL 描述的是同一个东西,把日文页的信号算到英文页头上。
日文那个 URL 不是被降权,是被代表掉了。索引里根本没有它的位置。哪怕它 hreflang 写得完全正确,哪怕它每天在 sitemap 里被提交一遍,都是这样。sitemap 只能告诉 Google “这个 URL 存在”,它推翻不了页面自己发出的归并声明。
批量查和改模板时会踩的坑
单页自检用 Search Console 的网址检查,看“用户声明的规范网址”那一栏是不是自己。十几个页面这样查还行,几百个页面就得换工具:用 Screaming Frog 之类爬一遍全站,把 Canonical Link Element 这一列拉出来按目录排,哪个目录下的 href 指向了别的目录,一眼就能看出来。
改模板通常只是改一行,但改完必须重爬确认。我吃过这个亏:改的是布局文件里的一个变量,只在部分模板分支上生效,/ja/ 下面的一批内容页还是老样子,因为那批页面走的是另一套模板。所以“改完了”和“全站都改完了”是两件事。
还有一层更隐蔽的冲突:hreflang 和 canonical 会打架。同一个页面用 canonical 说自己等于英文页,又用 hreflang 说自己是英文页的日文版,这是两个互相矛盾的信号。hreflang 是提示,canonical 也是,但当二者冲突时,决定 URL 归并的通常不是 hreflang。
而且 hreflang 必须双向。英文页要指回日文页,日文页要指回英文页,整个语言集群还要有一条 x-default 兜底。少一条回指,Google 经常直接忽略整个集群——你以为自己配了五种语言,实际一条都没生效。x-default 要写成“没有匹配语言时的兜底”,不要每个语言页面各写一个不一样的。
sitemap 按语言拆,是为了能读,不是为了好抓
再往下才是 sitemap。我的做法是按语言拆文件:/sitemap-en.xml、/sitemap-ja.xml、/sitemap-de.xml,每个文件里只放自己语言的 URL,绝不混装。理由不是“拆了更好抓”,而是拆了你才读得懂。分开提交之后,Search Console 里可以按 sitemap 看已编入索引的数量,你才知道到底是德文版出问题还是日文版出问题。所有语言塞在一份里,你面对的永远是一个混在一起的数字,英文版涨了,你还以为日文版在好转。
robots.txt 里把这几份都列出来,在 Search Console 里分开提交,覆盖率就是按语言读的,不是按站读的。用 sitemap index 也可以,但拆分粒度别变粗——粒度一粗,诊断能力就没了。
顺带说一句,hreflang 不要求这些 URL 出现在 sitemap 里。但如果两处都用了,它们描述的必须是同一套“语言-URL”映射:页面里写 ja,sitemap 里也写 ja,别一处 ja 一处 ja-JP,也别一边指这个 URL 一边指那个。
新语言目录最常见的死法是没有路
sitemap 有个天生的局限:它证明 URL 存在,不证明 URL 会被爬到。新语言目录最典型的问题就是“进得去,走不深”。爬虫从首页出发,语言切换器如果只在首页顶部、或者靠 JS 渲染、或者藏在菜单第三层,那 /ja/ 下面几百篇文章对它来说就是一片没有路的地方。
我的检查办法很土:假装自己是爬虫,从首页点起,数几步能到一篇最深的日文文章。超过三步,我就去补链接——导航栏该有语言入口,英文文章正文里该指到对应的日文文章,面包屑和相关阅读该用上。sitemap 是入口,内链才是路。
只有一版被索引时,按这个顺序查
所以我的排查顺序固定下来了,只在“发现某个语言版本没收录”的时候跑:
先看那个语言的页面,它自己的 canonical 是不是自引用。八成的问题停在这一步。
再看 hreflang 有没有回指,x-default 是不是只出现了一次。
再查 robots.txt 和页面头部的 noindex、X-Robots-Tag 有没有把整个目录挡住。
再确认这个目录有没有内链可达性,新目录尤其要看。
最后才轮到 sitemap 有没有按语言提交,以及 sitemap 里的语言标注和页面里的写法是否一致。
顺序很重要。反过来做,你会花两天时间调 sitemap,而问题在模板的第一行。
两个别指望
第一,Search Console 的数据有延迟。你改完 canonical 不会立刻看到数字变化,这个延迟是它报告机制的一部分,不是你可以拿来当验收时间点的东西。别把“多久之后应该好”当成一个可以承诺的期限——你能做的只是确认自己发出的信号是干净的,然后等。
第二,canonical 修对了只是拿回了“被当成独立页面看待”的资格,不代表每个语言版本都会进。如果用的是机器翻译,页面本身的质量照样影响 Google 愿不愿意单独收它。这件事没有捷径可绕。
现在我看任何一个多语言站,第一个动作不是查关键词,是打开某个语言页面的源码,搜 canonical。这一行对了,后面所有工作才有地方落;这一行错了,你写的每一篇日文文章,在 Google 眼里都只是英文页的一个副本。