GEO-020·第 02 期·内容与语义·20 / 132

hreflang 之外:多语言站点的抓取一致性

hreflang 校验通过,德语商品页仍可能被 AI 判成重复页,德语提问永远轮不到你。本文拆开语言目录、canonical 与 hreflang 的三角冲突,讲清站点地图、缺失页与内容对等的处理,给出五步对齐做法和验收口径。

7 分钟GEO内容与语义出海

本期主线先被看见:抓取、渲染与 AI 爬虫可访问性

德国站上线两个月,运营在 ChatGPT 里用德语问“适合通勤的防水骑行背包推荐哪个”,答案里三个品牌,没有他们。技术同事先查 hreflang:校验工具 0 报错,双向标签齐全。再往下翻才看到问题——德语商品页的 canonical 全指向英语页,语言切换器是个 JS 下拉,sitemap 里也只有英语 URL。标签是对的,路是断的。

hreflang 只声明“谁对应谁”,不声明“这一页能被抓到”;语言目录、canonical、sitemap 三套信号不一致时,AI 拿到的是英语页,德语提问就永远轮不到你。

hreflang 是一句声明,不是一条通路

hreflang 只做一件事:告诉引擎这几个 URL 是同一内容的不同语言或地区版本,好让结果给出对应语言的那一版。它不创建页面,不提高可抓取性,也不保证被发现。

一个只在 hreflang 里被提到、HTML 里没有链接、sitemap 里没有条目、robots 或 WAF 还挡着的德语页,对爬虫等于不存在。更糟的是,标注指向一个抓不到的目标,这条标注本身变成噪音。

检索型爬虫是按问题取页面的:候选来自链接、sitemap 与站外引用,它未必替你跑一遍语言匹配,多数时候只抓它找到的那个 URL。所以每个语言版本要能独立成立——独立 URL、独立可达、独立可读。

检验方法很土但有效:把 hreflang 全删掉,德语页还能不能被链接到、被抓到、被读懂?能,标签才有意义;不能,先修通路,别改标签。

三套信号打架时,AI 信的是 canonical

语言目录决定 URL 形状,canonical 决定这一页算谁的,hreflang 决定对应关系。三者里 canonical 语义最强:一页把 canonical 指向另一页,等于自己承认“我不是正本”。

硬规则一条:每个语言版本的 canonical 指向自己,hreflang 指向其他语言版本,并且双向闭合。canonical 指英语、hreflang 也指英语,是自相矛盾;引擎通常按 canonical 处理,德语页被并进英语页,hreflang 白写。

常见配置看起来没问题的地方AI 侧的实际结果
语言切换器只做 JS 或 cookie人点得动HTML 里没有指向德语页的链接,爬虫走不到
德语页 canonical 指向英语页hreflang 校验 0 报错德语页被当作重复页,不参与德语答案
sitemap 只有默认语言站点地图提交成功德语页只能靠切换器被发现
按 IP 或浏览器语言自动跳转用户体验顺爬虫被 302 回英语页,德语页不再被取
缺内容的语言页 302 到首页没有 404软 404,等于说这页不该存在

自动跳转单独说一句。按 IP 或浏览器语言把 /de/ 跳回 /,对人是省一步,对爬虫是把入口关上:它没有你的位置,也不带你的 cookie。真实语言页必须能在不带任何偏好时返回 200。这类 302 在日志里很好认,怎么把日志读成证据见一次完整的 AI 抓取审计。

sitemap 里,每个语言版本都要自己出现

sitemap 的 alternates 是 hreflang 的第二种写法:一条 <url> 挂一组 xhtml:link,或者每个语言各自成条。两种写法都行,判断标准只有一条:德语 URL 有没有作为一个可发现的条目出现。

清单五项:

  • 每个语言版本在 sitemap 里有自己的 <url>,不是只出现在 alternates 里
  • 每条 <url> 的 lastmod 反映该语言的实际更新,而不是英语页的更新时间
  • sitemap 按语言拆文件(/sitemap-de.xml),覆盖差异一眼能看出来
  • 提交后抽 20 条德语 URL,用 curl 访问,返回 200 且语言正确
  • alternates 里的 URL 与页面内 hreflang 完全一致,不出现两套写法

容易误判的一点是“提交成功”。sitemap 只解决发现,不解决优先级;如果德语页的全部入口只有 sitemap 一条,抓取顺序会被排到最后。补链接比补 sitemap 更直接:分类、筛选与分页里那条“三跳可达”,同样适用于语言目录。

内容对等:缺失页不能用机翻补齐

多语言站真正难的不是标签,是德语版有没有值得被引用的内容。200 个 SKU 的英语站,德语只做了 20 个的本地文案,其余 180 个用机翻铺页——这不是覆盖,是把 180 个薄页塞进德语索引,稀释掉那 20 个。

情况该怎么做不该怎么做
有本地需求,也有本地文案独立德语页,尺寸、保修、配送按当地写直接复用英语答案单元
有需求,没文案保留英语页并标明语言机翻生成空壳页
没需求,也没文案404/410,或本地化的“暂不配送”说明302 到英语首页

配套要改的是度量口径:看“德语有内容的 SKU 覆盖率”,不看“德语 URL 数”。后者只会鼓励铺页。

五步把三套信号对齐

步骤做什么谁来做怎么算过
1 盘点列出语言 × 模板矩阵,每格填一个真实 URL,标出有没有静态链接、在不在 sitemap、canonical 指向谁工程矩阵每格都有样本;空白的格子就是缺口
2 修三角canonical 改自指,hreflang 双向闭合,x-default 指向默认语言或语言选择页工程抽 20 个 URL,双向都对得上,canonical 全部自指
3 补通路切换器改静态链接,sitemap 每语言独立成条与成文件,取消按 IP 或语言的自动跳转工程无 JS、无 cookie 抓取仍返回 200 的德语页
4 定缺失页按上表三档处理,禁止无人工校对的机翻铺页内容德语 SKU 覆盖清单,缺口有明确处置
5 复测同一批德语问题复测引用落地页的语言与 URL增长引用页语言与提问语言一致的比例,按月记

第 1 步最容易被跳过,因为“我们有几个语言”听起来不需要盘点。真实站点里,语言目录常常是历史遗留:/de/ 与 /de-de/ 混用,部分页面来自旧站迁移,子域名和子目录并存。矩阵列出来之前,别急着改标签。

示例:一次德语站上线前检查

示例:一家做骑行配件的独立站(假设场景,不含真实品牌与数据),英语站 400 个 SKU,德语站两个月前上线,结构是 /de-de/。

检查出了三件事:德语页只作为 alternates 出现在英语条目的 sitemap 里;语言切换器是 JS 下拉,HTML 里没有链接;德语商品页的 canonical 指回英语页。改法对应三步:canonical 改自指、hreflang 补成双向闭合;sitemap 拆出 /sitemap-de.xml,每个德语 URL 独立成条;切换器改成静态 <a>,页脚同时给一条语言链接。

内容侧只做了 60 个 SKU 的德语文案,其余 340 个不铺机翻页,返回带德语说明的 404。验收看三件:无 JS 抓德语首页能顺着链接走到德语商品页;德语提问的引用落地页以 /de-de/ 开头;canonical 与 hreflang 双向一致。三件都过,再往上加 SKU。

边界:什么情况下这套对齐不适用

只做一个语言市场时不必做。英语站只卖美国,重点在抓取、渲染与答案单元,多语言一致性是空转。

成交发生在平台内的品类,先修 listing 与 feed。亚马逊、Shopee 的语言版本由平台管,官网 hreflang 与那些答案无关,三种载体的可访问性讲的是另一条线。

本地供给还没建起来时,不要先铺语言页。德语站上线了,但支付、退货、客服还只有中文团队,AI 把买家带过来只会放大退货。GEO 放大已有的供给,不制造供给。

用 ccTLD 的老站,历史结构改不动就别为了统一而全站迁移。先把 canonical 与 sitemap 对齐,URL 形状留在原地。

最后一种误用是把 hreflang 当排名工具。它解决的是“给哪个语言版本”,不解决“排第几”,也不解决“被不被引用”。

先做一件事:挑一个非默认语言目录,用 curl 抓 20 个 URL,不带 JS、不带 cookie、带上该语言的 Accept-Language,记三列——是否返回 200 且是该语言内容、canonical 是否自指、hreflang 是否双向闭合。出现“否”的那一行,就是本月要修的第一批;修完用同一批该语言的提问复测引用落地页,基线口径见AI 可见度基线。这条线的其余文章在内容与语义主题页。