Search Console「International Targeting」报告(旧版工具里)显示红色警告:“No return tags”、“Invalid language code”、“URL not found”。翻译上线几周了,错误仍在。让人沮丧的是,报告并不告诉你具体哪一对页面触发了哪一条——只有总数和一个示例 URL。本文解读每种错误类型、以及怎么批量修。
通用 hreflang 概念概览,看 hreflang 警告快速指南。
常见原因
按命中率从高到低。
1. “No return tags”——A 链 B 但 B 不回链
hreflang 必须双向。/en/article/ 声明 hreflang="zh" 指向 /zh/article/,那 /zh/article/ 必须声明 hreflang="en" 指回。不对称的声明 Google 会静默忽略。
怎么判断:
# 对照一对页面
curl -s https://site.com/en/article/ | grep -oE 'hreflang="[^"]+" href="[^"]+"'
curl -s https://site.com/zh/article/ | grep -oE 'hreflang="[^"]+" href="[^"]+"'
/en/ 有 4 行 hreflang、/zh/ 只有 1 行,缺的 3 个就会触发 “no return tags”。
2. “Invalid language code”——格式错
常见错误:
- 写
hreflang="zh"而不是zh-CN或zh-Hans(Google 要求中文用更具体的码) hreflang="en_US"(下划线,必须用横杠en-US)hreflang="english"(全称,应该用 ISO 码)hreflang="zh-CHS"(用旧的微软 locale 码而不是 ISOzh-Hans)
怎么判断:
curl -s https://site.com/en/article/ | grep -oP 'hreflang="\K[^"]+' | sort -u
每个对照 有效 ISO 639-1 码(可选 + ISO 3166-1 地区)。
3. “URL not found”——hreflang 目标返回 404
加了 hreflang 指向 /de/article/ 但德语版还没建,或者 URL 不同。目标返回 404(或 301 别处)hreflang 就失败。
怎么判断:
for url in $(curl -s https://site.com/en/article/ | grep -oP 'hreflang="[^"]+" href="\K[^"]+'); do
echo -n "$url: "
curl -sI "$url" | head -1
done
任何非 200 = hreflang 坏了。
4. hreflang 声明的 URL 不是 canonical
你 hreflang 指向 /zh/article?ref=newsletter,但那个页面的 canonical 是 /zh/article/。Google 当作 hreflang 指向了非 canonical URL 并警告。
怎么判断:每个 hreflang URL 都对照它的 canonical。必须完全一致。
5. 缺 x-default
严格说不是错误,但接近警告:没有 x-default,对于 locale 不匹配任何已声明 hreflang 的用户,Google 没有 fallback。
怎么判断:看有没有 <link rel="alternate" hreflang="x-default" href="..." />。没有就加,指向你的主语言版本。
6. hreflang 同时存在于 <head> 和 HTTP 头
HTML <link> 标签里加了 hreflang,又在 HTTP Link 头(或 sitemap)里也加了。Google 三个都读但冲突会触发警告。
怎么判断:
curl -sI https://site.com/en/article/ | grep -i link:
HTML <link rel="alternate"> 和 HTTP Link 头都在发 hreflang,选一个(HTML 最常用)。
最短修复路径
第 1 步:从单一源生成 hreflang
不要每页手写 hreflang。从 translation-key map 生成:
// site.config.mjs
export const locales = {
en: { hreflang: 'en', urlPrefix: '/en/' },
zh: { hreflang: 'zh-Hans', urlPrefix: '/zh/' },
};
export function hreflangAlternates(translationKey) {
return Object.entries(locales).map(([lang, { hreflang, urlPrefix }]) => ({
hreflang,
href: `https://site.com${urlPrefix}${translationKey}/`,
})).concat([{ hreflang: 'x-default', href: `https://site.com/en/${translationKey}/` }]);
}
layout 里循环这个列表吐 <link rel="alternate"> 标签。单一可信源 = 不可能不对称。
第 2 步:用完整 locale 码
大多数中日韩和地区变体,优先用显式形式:
| 错 | 对 |
|---|---|
zh | zh-Hans(简体)或 zh-Hant(繁体) |
pt(歧义) | pt-BR 或 pt-PT |
en_US | en-US |
english | en |
ISO 639-1 + 可选 ISO 3166-1 地区。无空格、无下划线。
第 3 步:校验所有 hreflang URL 都返回 200
# CI 脚本——从 sitemap 抽所有 hreflang URL 逐一校验
xmllint --xpath '//*[local-name()="link"]/@href' sitemap.xml | grep -oP 'href="\K[^"]+' | while read url; do
status=$(curl -s -o /dev/null -w "%{http_code}" "$url")
[ "$status" = "200" ] || echo "BAD: $status $url"
done
第 4 步:加 x-default
永远包含:
<link rel="alternate" hreflang="x-default" href="https://site.com/en/article/" />
指向你的主 / fallback 语言版本。
第 5 步:只用一种发布机制
只选一个:
- HTML
<link>标签 放在<head>——静态站最简单 - HTTP
Link头——非 HTML 资源(PDF 等)最合适 - Sitemap
<xhtml:link>——超大站合适
不要三个都用。选一个坚持。
第 6 步:提交 sitemap 后等
修后重新生成 sitemap、在 Search Console → Sitemaps 重交。International Targeting 报告 1-3 周内更新。
第 7 步:用工具验证
把 URL 扔进 hreflang 校验器 或 Aleyda Solis 的工具。它们能精确指出哪一对不对称。
预防建议
- 从单一
translationKey源生成 hreflang,永远不要每页手写。 - CI 校验 hreflang:每个 URL 必须返回 200、每个码必须有效、每对必须对称。
- 总是包含
x-default指向主语言版本。 - 只选一种发布机制(HTML link 标签最常用),不混用。
- 加新 locale 时,审已有页面确保它们都吐了新 locale 的 hreflang。