在上一篇文章中,我们介绍了通过 navigator.languages 读取浏览器的语言设置,在访问者语言非日语时自动将首页跳转至 /en/ 或 /zh/ 的机制。这一次,我们撤除了该机制。促成这一决定的是 Google Search Console 的一项判定。
观察到的事实
在 Google Search Console 的“页面索引”报告中,本站英文首页(https://kobayaxi.com/en/)出现了以下状态。
重复网页(Google 选择的规范网页与用户指定的不同)
该帮助页面对此状态的产生条件说明如下。
相应网页被标记为一组网页的规范网页,但 Google 认为另一网址更适合作为规范网页。Google 已将其认为是规范网页的那个网页(而非该网页)编入索引。
/en/ 的 HTML 通过 <link rel="canonical" href="https://kobayaxi.com/en/"> 将自身声明为规范网址,因此这一状态意味着:Google 采用了另一个网址(很可能是日文首页 /)作为规范网址,而非站点声明的那个。
我们首先怀疑是 canonical 与 hreflang 标签本身的实现有误。检查实际提供的 HTML 后,情况如下。
| 项目 | /(日文) | /en/(英文) |
|---|---|---|
| canonical | https://kobayaxi.com/ | https://kobayaxi.com/en/ |
| hreflang=ja | https://kobayaxi.com/ | https://kobayaxi.com/ |
| hreflang=en | https://kobayaxi.com/en/ | https://kobayaxi.com/en/ |
| hreflang=zh-CN | https://kobayaxi.com/zh/ | https://kobayaxi.com/zh/ |
| hreflang=x-default | https://kobayaxi.com/ | https://kobayaxi.com/ |
各页面均正确地自我指向为 canonical,hreflang 之间也相互一致。我们排除了标签实现有误这一假设,转而检视此前在首页实现的自动跳转逻辑。
原因与机制的考察
Google Search Central 的《How Google crawls locale-adaptive pages》一文,对 Googlebot 抓取时的行为说明如下。
Googlebot crawls with IP addresses based outside the USA, in addition to the US-based IP addresses…the crawler sends HTTP requests without setting
Accept-Languagein the request header.(Googlebot 除了使用美国境内的 IP 地址外,也会使用美国境外的 IP 地址进行抓取……抓取程序发送的 HTTP 请求中不会设置 `Accept-Language` 请求头)
此前的跳转逻辑会读取 navigator.languages(浏览器保存的按优先级排列的语言列表,通常来自 Accept-Language 设置),若列表首位不以 "ja" 开头,就会通过 location.replace() 跳转到 /en/ 或 /zh/。但如上所述,Googlebot 并不会在请求中发送 Accept-Language 请求头。既然 navigator.languages 在该设置缺失时,可能直接返回渲染环境自身的默认值,那么 Googlebot 执行这段脚本时会走向哪个分支,就不是网站一方可以控制或预测的了。
Google 在《Localized versions of your pages》中,对这类自动跳转给出了明确的提醒。
Avoid automatically redirecting users from one language version of a site to a different language version of a site. For example, don’t redirect based on what you think the user’s language may be. These redirections could prevent users (and search engines) from viewing all the versions of your site.
(应避免将用户从网站的一个语言版本自动跳转到另一个语言版本。例如,不要仅凭你对用户语言的猜测就进行跳转。这类跳转可能导致用户(以及搜索引擎)无法查看网站的所有版本。)
撰写上一篇文章时,我们已经了解这一提醒,并据此采取了以下三项措施:
- 将自动跳转限定在首页,其他页面的链接保持不变
- 在所有页面输出 hreflang,明确各语言版本之间的对应关系
- 通过 x-default 为不符合任何语言条件的访问者指定默认页面
然而,这三项措施针对的都是“向 Google 说明存在哪些语言版本、彼此如何对应”,并未消除首页本身发生的跳转。Google 所担忧的问题——搜索引擎可能无法查看网站的所有版本——只要自动跳转仍然存在,就不会因为 hreflang 的完善而得到解决。
需要说明的是,仅凭 Search Console 的显示结果,无法验证 Google 内部究竟经过怎样的处理,才将 /en/ 的规范网址替换为其他网址。以上内容是基于 Google 已公开的规范、建议与本站实现方式相互比对后得出的推断,并非确定的结论。
解决方法
我们撤除了限定于首页的自动跳转逻辑,以及与之配套的“将语言切换链接的点击结果记录到 localStorage、并在后续访问中优先于自动判断”的逻辑。
撤除前,嵌入在首页 <head> 中的逻辑如下。
;(() => {
try {
if (document.referrer && new URL(document.referrer).origin === location.origin) return
const target = (l) => (l === 'en' ? '/en/' : l === 'zh' ? '/zh/' : null)
const stored = localStorage.getItem('preferred-lang')
if (stored) {
const t = target(stored)
if (t) location.replace(t)
return
}
for (const l of navigator.languages || [navigator.language]) {
const s = (l || '').toLowerCase()
if (s.startsWith('ja')) return
const t = target(s.slice(0, 2))
if (t) { location.replace(t); return }
}
} catch {}
})()撤除后,首页的 <head> 中不再包含这段逻辑。我们同时移除了将语言切换点击记录到 localStorage 的脚本,以及仅供该脚本使用的 data-lang-switch 属性。此后,各语言版本仅通过页眉、页脚中常规的语言切换链接(指向 /、/en/、/zh/ 的静态链接)以及各页面的 hreflang 标签进行引导。
小结
- Google Search Console 出现“重复网页(Google 选择的规范网页与用户指定的不同)”,并不一定意味着 canonical、hreflang 标签的实现有误
- 由于 Googlebot 抓取时不会在 HTTP 请求中发送
Accept-Language,基于浏览器语言设置的跳转,对 Googlebot 而言可能产生非预期的行为 - Google 明确建议避免基于对用户语言的猜测进行自动跳转,完善 hreflang 并不能替代遵循这一建议
- 作为解决方法,我们撤除了限定于首页的自动跳转及相关的
localStorage逻辑,仅保留手动的语言切换链接与 hreflang 标签