每一页都声明了 hreflang——en 指向英文版、zh 指向中文版、两者互引。Search Console 国际定位报告(或第三方 hreflang 校验工具)仍然报 missing x-default。更糟的是:印度、巴西、德国的用户搜你的品牌,落在中文页上,秒退,跳出率直线上升。
x-default 是 hreflang 的一个特殊值,决定声明语言之外的用户看到什么。一旦你的受众超出已声明的语言范围,它就不是可选项了。这篇文章讲清楚 x-default 到底做什么、哪些写法会出问题、以及可以直接复制的修复方法。
x-default 到底是什么意思
按 Google 文档:x-default 声明的是当用户浏览器语言偏好与所有声明的 hreflang 都不匹配时,该呈现的页面。它不是”爬虫的 fallback”,也不是”网站首页”,而是当前这页内容的语言无关默认版本。
举例:德国用户用 Accept-Language: de 访问 yourdomain.com/article。你声明了 en 和 zh,没有 de。没有 x-default 时,Google 自行挑——通常根据外链数等信号挑,往往挑错。配上 x-default: en 后,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)
Search Console 国际定位报”missing 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 条目。
最短修复路径
按命中率排序:
- 缺什么补什么——先把
x-default加上 → 立刻消掉 Search Console 警告,下次抓取后第三语言区用户被稳定送到英文页。 - 核对
x-default指的是当前页对等版本而不是首页 → 修掉那条静默漏水的 UX 通道。 - 从单一 helper 重新生成全站 hreflang 标签 → 防止模板间漂移。
- 1–2 周后用 Search Console “国际定位”报告复验 → 确认 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);
}
预防建议
- hreflang 永远从一个 helper 输出——不允许各模板手写。
x-default指向当前页的对等版本,不是站点首页。x-default的目标 URL 必须直接返回 200,绝对不能是重定向链。- 每次改完模板,用 Search Console 国际定位报告复验。
- 加第三种语言时,改 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。
Q:Search Console 报”hreflang tag is invalid”,是 x-default 引起的吗?
A:有可能。最常见原因:(1) 缺 x-default;(2) x-default 指向 4xx URL;(3) 目标 URL 没有反向引用(x-default 指向的那页,自己的 hreflang 块里必须包含这一页)。完整诊断流程见 Search Console 的 hreflang 警告。