最快修复: 每页补一行 <link rel="alternate" hreflang="x-default" href="...">,指向当前这一页的英文版(不是首页,也不是会跳转的 URL)。这样就能消掉”missing x-default”告警,也不再让 Google 替声明语言之外的用户瞎猜该显示哪种语言。
每一页都声明了 hreflang:en 指向英文版、zh 指向中文版、两者互引。可 hreflang 校验工具(Merkle、TechnicalSEO.com、Ahrefs Site Audit 或 Screaming Frog)仍然报 missing x-default。更糟的是:印度、巴西、德国的用户搜你的品牌,落在中文页上,秒退,跳出率直线上升。
x-default 是 hreflang 的一个特殊值,决定声明语言之外的用户看到什么。一旦你的受众超出已声明的语言范围,它就不是可选项了。这篇讲清楚 x-default 到底做什么、哪 6 种写法会出问题、以及可以直接复制的修复方法。
有一点和过去不一样了: Google 已于 2022 年 9 月把 International Targeting(国际定位)报告从 Search Console 中移除,因此不再有内置的全站 hreflang 报告。hreflang 本身仍被完整支持,只是那个报告没了。现在要靠 URL 检查工具(逐 URL)加上第三方爬虫(全站)来校验。下文的验证步骤都已按这个现状给出。
x-default 到底是什么意思
按 Google 文档:保留值 x-default 用于”当用户浏览器语言偏好与所有声明的语言/地区都不匹配时”该呈现的页面。它不是”爬虫的 fallback”,也不是”网站首页”,而是当前这页内容的语言无关默认版本;Google 同时点名它适用于按浏览器语言或 IP 分流的语言选择页 / 入口页。
举例:德国用户用 Accept-Language: de 访问 yourdomain.com/article。你声明了 en 和 zh,没有 de。没有 x-default 时,Google 自行挑——通常按外链数等信号挑,往往挑错。把 x-default 配成英文页后,Google 会稳定把这个德国用户送到英文页。
先判断你属于哪种情况
情况 1:完全没有 x-default
怎么发现:
curl -s https://yourdomain.com/en/article/ | grep hreflang
# 返回:
# <link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/">
# <link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/">
# (没有 x-default)
hreflang 校验工具(Merkle、TechnicalSEO.com、Screaming Frog)报 “missing x-default” / “no x-default” 警告。en 和 zh 用户体验正常,但第三语言区的用户会被随机分流。
原因:大多数 CMS 的 hreflang 插件按已声明语言逐条生成 link,没人配 x-default 就不会自动加。手写模板也常忘。
修复:补一条 x-default,指向你的默认版本(通常是英文):
<link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/">
<link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/">
<link rel="alternate" hreflang="x-default" href="https://yourdomain.com/en/article/">
x-default 完全可以(也通常应该)和某个声明语言指向同一 URL。同一个英文页 URL 出现两次——一次是 hreflang="en",一次是 hreflang="x-default",这是正确写法。
情况 2:x-default 指向网站首页,而不是当前页的对等版本
怎么发现:在深层文章 URL 上查看源代码,看 hreflang 块:
<!-- 当前在 /en/article/some-post -->
<link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/some-post/">
<link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/some-post/">
<link rel="alternate" hreflang="x-default" href="https://yourdomain.com/"> <!-- 错 -->
原因:有人把规范理解成”x-default 就是站点首页”。其实不是——x-default 是当前页的语言无关默认版本。
后果:德国用户从搜索结果里点进”你这篇文章主题”,落到首页而不是要看的那篇文章,秒退。声明语言之外的所有长尾访问都在流失。
修复:x-default 必须指向当前页的对等版本。在 /en/article/some-post/ 上,x-default 指向 /en/article/some-post/,不是 /。
情况 3:x-default 指向了一个重定向
怎么发现:
curl -sI "https://yourdomain.com/article" | head -1
# HTTP/2 301
你的 x-default 写的是 https://yourdomain.com/article,但这个 URL 301 跳到 https://yourdomain.com/en/article/。Google 会跟着跳,但信号会被削弱,部分校验工具会直接报错。
原因:早期 URL 结构里 /article 就是英文页,后来引入 /en/ 和 /zh/ 子目录、加了重定向,但 x-default 没同步更新。
修复:x-default 直接指向最终目的地,别指向重定向中转 URL。
情况 4:同站不同页的 x-default 写法不一致
怎么发现:爬一遍 sitemap,把所有页面的 x-default 抓出来对比:
node -e "
const xml = await (await fetch('https://yourdomain.com/sitemap.xml')).text();
const urls = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map(m => m[1]);
for (const u of urls) {
const html = await (await fetch(u)).text();
const m = html.match(/hreflang=[\"']x-default[\"']\s+href=[\"']([^\"']+)[\"']/);
console.log(u, '->', m ? m[1] : '(missing)');
}
" > x-default-audit.tsv
排序后看异常项。如果 90% 的页面 x-default 都指向 /en/ 对等页,剩几条指向 /zh/ 或首页,那几条就是错的。
原因:CMS 插件升级换了默认行为;或者有几页被人手动改过。
修复:从单一来源重新生成全站 hreflang(见下文”最短修复路径”)。
情况 5:sitemap 的 x-default 与 head 里的 x-default 打架
怎么发现:页面 HTML 写 x-default = /en/article/,但 sitemap.xml 通过 <xhtml:link> 声明的是 x-default = /zh/article/。Google 报警告并任意挑一个。
原因:两个不同系统在分别输出 hreflang(CMS 输出页头标签、另一套独立的 sitemap 生成器),结果不一致。
修复:hreflang 只从一处输出,二选一——要么页头标签,要么 sitemap 条目。
情况 6:x-default 看着有,但语言/地区码非法
怎么发现:x-default 那行在、页面”结构上”也能通过,但各语言条目其实毫无作用。把页面丢进 Merkle 的 hreflang 测试工具 或 Screaming Frog,看有没有 “incorrect language/region codes”。
<!-- 错:en-UK 不是合法地区码 -->
<link rel="alternate" hreflang="en-UK" href="https://yourdomain.com/en/article/">
<link rel="alternate" hreflang="zh_CN" href="https://yourdomain.com/zh/article/">
原因:hreflang 用的是 ISO 639-1 语言码加 ISO 3166-1 alpha-2 国家码,中间用连字符连接。两个坑几乎人人踩:
- 英国的国家码是
GB,不是UK。en-UK非法,要写en-GB。 - 分隔符是连字符,不是下划线。
zh_CN非法,要写zh-CN(不按地区拆分就直接用zh)。
Google 会静默忽略非法码——标签照样渲染,却不携带任何信号,于是这个语言(以及它在 x-default 逻辑里该占的份额)等于消失了。这正是”明明有 hreflang 还是路由错”的常见根因。
修复:语言码小写、地区码大写、中间连字符:en、zh、en-GB、zh-CN、pt-BR。x-default 原样写成 x-default(它是唯一允许的非语言取值)。
诊断速查表
动手改之前,先把症状对到具体情况。
| 症状 | 对应情况 | 先查什么 |
|---|---|---|
| 校验工具报 “missing x-default” | 情况 1 | curl ... | grep x-default 没有任何输出 |
| 第三语言区用户落到了首页 | 情况 2 | x-default href 以 / 结尾,而不是文章路径 |
| 校验报 x-default URL 有重定向 | 情况 3 | curl -sI <x-default-url> 返回 301/302 |
| 大部分页路由正常,少数错 | 情况 4 | 全站 x-default 审计能看出离群项 |
| 页头与 sitemap 不一致 | 情况 5 | 对比 <head> 标签与 sitemap.xml 的 <xhtml:link> |
| hreflang “有”,但某语言被忽略 | 情况 6 | 校验报 “incorrect language/region code” |
最短修复路径
按命中率排序:
- 缺什么补什么——先把
x-default加上 → 消掉校验工具的警告,下次抓取后第三语言区用户被稳定送到英文页。 - 核对
x-default指的是当前页对等版本而不是首页 → 修掉那条静默漏水的 UX 通道。 - 确认每个语言码都合法(
en、zh、en-GB,而不是en-UK/zh_CN)→ 别让 Google 把某个语言静默丢掉。 - 从单一 helper 重新生成全站 hreflang 标签 → 防止模板间漂移。
- 用爬虫 + URL 检查工具重新校验 → 确认 Google 现在读到的是一致且互引的 hreflang。
hreflang 的单一来源
把所有 hreflang 和 x-default 都从一个函数生成,让每页结构一模一样:
// src/lib/hreflang.js
const BASE = "https://yourdomain.com";
const DEFAULT_LANG = "en";
export function buildHreflang(currentLang, slug, availableLangs) {
const tags = availableLangs.map((lang) => ({
hreflang: lang,
href: `${BASE}/${lang}/${slug}/`,
}));
tags.push({
hreflang: "x-default",
href: `${BASE}/${DEFAULT_LANG}/${slug}/`,
});
return tags;
}
在 layout 里:
---
import { buildHreflang } from "../lib/hreflang.js";
const tags = buildHreflang(lang, slug, ["en", "zh"]);
---
{tags.map((t) => (
<link rel="alternate" hreflang={t.hreflang} href={t.href} />
))}
每页输出形状统一:每种语言一条,外加一条 x-default 指向当前页的英文对等。零手工。
构建期检查
加一个 prebuild 检查,HTML 产物缺 x-default 时构建失败:
// scripts/check-x-default.mjs
import fg from "fast-glob";
import fs from "node:fs";
const files = fg.sync("dist/**/*.html");
const missing = [];
for (const f of files) {
const html = fs.readFileSync(f, "utf8");
const hasHreflang = /hreflang=["'](en|zh)["']/.test(html);
const hasXDefault = /hreflang=["']x-default["']/.test(html);
if (hasHreflang && !hasXDefault) missing.push(f);
}
if (missing.length) {
console.error("Missing x-default:\n" + missing.join("\n"));
process.exit(1);
}
如何确认已修好
- 逐 URL: 在 Search Console 里打开 URL Inspection(URL 检查),粘进线上 URL,查看抓取到的 HTML——确认
x-default那行在,且指向正确的当前页英文 URL。 - 单页快查: 把 URL 粘进 Merkle 的 hreflang 测试工具。它会读 HTML 标签、HTTP 头和 sitemap 条目,并标出缺失的反向引用、错误的码和缺失的
x-default。 - 全站: 用 Screaming Frog(或 Ahrefs Site Audit)爬一遍,筛 hreflang 问题。目标是零 “missing x-default”、零 “non-reciprocal”、零 “incorrect language/region code”。
- 互引: 每一对页面都要确认两边互列对方。Google 的规矩很直白:“If two pages don’t both point to each other, the tags will be ignored.”(两页不互指,标签直接被忽略。)
预防建议
- hreflang 永远从一个 helper 输出——不允许各模板手写。
x-default指向当前页的对等版本,不是站点首页。x-default的目标 URL 必须直接返回 200,绝对不能是重定向链。- 只用合法码:ISO 639-1 语言码小写、ISO 3166-1 alpha-2 地区码大写(
en、en-GB),中间连字符。 - 每次改完模板,用爬虫 + URL 检查工具重新校验(旧的 GSC 国际定位报告已下线)。
- 加第三种语言时,改 helper,而不是 1000 个模板。
FAQ
Q:x-default 必填吗?
A:严格说不是——Google 没它也能工作。但只要你的受众覆盖声明语言之外(基本上所有面向公众的站都是),就强烈建议加上。没有它 Google 自行猜,猜错的概率不小。
Q:x-default 能与某个声明语言指向同一 URL 吗?
A:可以——这是标准写法。通常 x-default 和 hreflang="en" 都指向英文页。一个 URL,两条 <link>,都合法。
Q:x-default 会自动跳转用户吗?
A:不会。x-default 只告诉 Google 在搜索结果里给无匹配语言的用户呈现哪个版本,本身不执行重定向。需要按 Accept-Language 做浏览器层路由,得另外实现。
Q:我的 hreflang 写在 sitemap 里、不在 head 里。还需要 x-default 吗?
A:需要。sitemap.xml 里的 xhtml:link 走的是同一套规则——按语言列条目时也要带一条 x-default。但别同时从 head 和 sitemap 两处都输出 hreflang(见情况 5)。
Q:Search Console 把国际定位报告下线了,现在怎么校验 hreflang? A:Google 已于 2022 年 9 月撤掉那个报告,但仍完整支持 hreflang。逐 URL 用 **URL Inspection(URL 检查)**工具看抓取到的 HTML;全站用第三方爬虫——Merkle/TechnicalSEO.com、Ahrefs Site Audit 或 Screaming Frog。它们能查出旧报告查的那些错,外加互引和错误码检查。
Q:校验工具报 “incorrect language/region code”,这算 x-default 的问题吗?
A:通常是另一个相关 bug。英国国家码是 GB(en-UK 非法),码之间用连字符(zh-CN,不是 zh_CN)。Google 会静默忽略非法码,标签虽渲染却不携带任何信号。先把码改对,再重新校验(见情况 6)。
Q:工具报 “hreflang tag is invalid”,是 x-default 引起的吗?
A:有可能。最常见原因:(1) 缺 x-default;(2) x-default 指向 4xx 或会重定向的 URL;(3) 目标没有反向引用——x-default 指向的那页,自己的 hreflang 块里必须包含这一页,否则 Google 两边都忽略。完整诊断流程见 Search Console 的 hreflang 警告。