# General Translation Knowledge Base: 语言重定向与 SEO：URL、cookie 还是 Accept-Language？
URL: https://generaltranslation.com/zh/kb/locale-routing-seo.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: 凡是希望被搜索引擎收录的页面，都应由 URL 决定其语言，因为 Googlebot 不会发送 Accept-Language 请求头，而且大多从美国 IP 地址抓取。只有在决定 / 这类中性根路径重定向到哪里时，才应优先参考 cookie 中保存的用户选择，其次是 Accept-Language，并使用临时的 302 或 307 重定向。
Category: 多语言 SEO
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

这三种信号各有用处，但回答的是不同的问题：URL 表明请求的是哪个版本，cookie 表明该访问者此前做过什么选择，`Accept-Language` 则反映其浏览器的语言设置。只有 URL 在爬虫和访问者眼中完全一致，因此页面内容必须由它来决定。

## 为什么每种语言都需要独立的 URL？

Google 关于[区域设置自适应页面](https://developers.google.com/search/docs/specialty/international/locale-adaptive-pages)的指南解释了这个问题。如果页面根据推断出的访问者国家或语言返回不同内容，Google 可能无法抓取、索引所有版本或为其排名，因为 Googlebot 的默认 IP 地址显示位于美国，而且爬虫发送请求时不带 `Accept-Language` 请求头。Google 建议为每个区域设置使用独立的 URL，并通过 [hreflang](/kb/what-is-hreflang) 进行标注。

Googlebot 确实也会从美国以外的 IP 地址进行抓取，但 Google 的[多区域网站指南](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites)指出，Google 不会为了发现网站的不同版本而改变爬虫的位置。该指南建议为每种语言使用不同的 URL，“而不是使用 cookie 或浏览器设置”。

这会带来切实的影响。假设 `/pricing` 对偏好德语的浏览器返回德语，对其他所有访问者返回英语。Googlebot 访问时既不带 `Accept-Language`，也没有 cookie，因此拿到并索引的都是英语版本。德语版本虽然对用户可用，但 Google 可能根本无法发现或索引它。

## 各信号的适用场景

| 信号                   | 它能告诉你什么           | 爬虫发送的内容                      | 适用于                 |
| -------------------- | ----------------- | ---------------------------- | ------------------- |
| URL (`/de/preise`)   | 所请求的版本            | 它在链接、站点地图或 hreflang 中发现的 URL | 决定返回的内容：可被索引的唯一可信来源 |
| Cookie (`locale=de`) | 该访问者之前选择的语言       | 不包含任何访问者的选择                  | 在后续访问时记住用户的明确选择     |
| `Accept-Language`    | 浏览器的 UI 语言及已配置的偏好 | 无 (Googlebot 不发送该请求头)        | 在首次访问时建议一种语言        |
| IP 地理定位              | 大致所在国家，而非语言       | 大多为美国地址                      | 建议一个地区；通常得不偿失       |

浏览器请求头远没有看上去那么可靠。MDN 的 [`Accept-Language` 参考](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language)将其称为一种提示，指出它往往不受用户控制 (例如在旅行时) ，提到 Safari 和 Chrome 的无痕模式只会列出一种语言，并明确表示服务器绝不应覆盖用户明确做出的语言选择。IP 则更不可靠：Google 称 IP 位置分析“难度大，且通常不可靠”。 (参见[为什么 IP 地理定位是错误的信号](/kb/detect-user-language)。)

## 优先级：先 URL，再已保存的选择，最后是请求头

当这些信号相互冲突时，按以下顺序解析：

1. **URL。** 对 `/de/preise` 的请求始终返回德语内容，无论来自哪个浏览器、请求多少次都是如此。这能确保每个 URL 的内容保持稳定，而爬虫和缓存正依赖于这一点。
2. **已保存的选择。** 即访问者选择语言时设置的 cookie 或账户设置。它可以决定中性根路径将访问者引导至何处，或建议哪个替代版本，但不会改写访问者点击的本地化 URL。[语言切换器](/kb/language-switcher)应标明当前显示的语言。
3. **`Accept-Language` 请求头。** 仅在没有已保存的选择时使用，且只用于从根路径选择跳转目标或提供建议。
4. **默认区域设置。** 没有任何匹配项时的后备选项，Googlebot 获取的也是这一版本。

在切换器中选择某种语言会跳转到该语言对应的 URL，因此适用规则 1。

当优先级较低的信号与 URL 不一致时，应向访问者提供另一版本，而不是直接切换。例如显示一条“This page is available in Deutsch”的横幅，既尊重访问者点击的链接，也照顾到他们可能偏好的语言。有关读取这些信号的具体方法，请参阅[如何检测用户的浏览器语言](/kb/detect-user-language)。

## 是否应根据浏览器语言重定向访问者？

常见的错误做法是根据请求头或 IP 重定向本地化 URL：使用法语浏览器的访问者打开 `/en/pricing` 时，会被强制跳转到 `/fr/tarifs`。Google 的指南明确建议网站避免这种做法，并指出根据推测的用户语言进行重定向，可能导致用户和搜索引擎无法查看网站的所有版本。想看英文页面的法语用户无法访问英文页面，而来自美国地址的爬虫则只能访问英文版本。

可以用以下两种方式替代：

* **在各语言版本之间互相链接。** Google 建议将每个页面链接到其他语言版本，让用户自行选择。在每个页面上放置语言切换器，也能为爬虫提供访问所有版本的路径。
* **只在中立的根路径上做一次重定向。** 像 `/` 这样本身没有内容的根 URL，可以根据用户已保存的选择或请求头进行重定向，也可以显示语言选择器。你可以在首页的 hreflang 集合中将其标注为可选的 `x-default`。Google 将语言选择器和自动重定向的首页列为适用场景；而深层页面应针对自身内容保留相关的后备内容。

### 根路径应使用哪种状态码

应使用临时重定向，即 `302` 或 `307`，因为目标地址取决于请求者是谁。Google 的[重定向指南](https://developers.google.com/search/docs/crawling-indexing/301-redirects)对临时路由与永久性 URL 迁移作了区分：临时重定向倾向于保留源 URL，而永久重定向 `301` 或 `308` 则是优先采用目标 URL 的强烈信号。因此，如果目标地址只是因访问者而异的选择，从与语言无关的根路径进行永久重定向就会传递错误的信号。

## 响应内容可变时的缓存

缓存按 URL 存储响应。如果同一个 URL 会根据请求头或 cookie 返回不同语言，CDN 就可能把上一位访问者的语言版本返回给下一位访问者。HTTP 通过 `Vary` 响应头来解决这一问题：

* **`Vary: Accept-Language`** 会让缓存以“URL + 该请求头”作为响应的键，详见 [MDN 缓存指南](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching#vary)。该请求头的取值五花八门 (`en-US,en;q=0.9` 和 `en-GB,en;q=0.9,de;q=0.8` 就是不同的键) ，因此缓存命中率会下降。
* **`Vary: Cookie`** 的问题更严重。根据 [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111#section-4.1)，只有当指定的请求头完全匹配时，才会复用已存储的响应，而 `Cookie` 请求头会携带所有 cookie，包括会话和分析相关的 cookie。对于个性化响应，MDN 建议使用 `Cache-Control: private`，而不是按 cookie 区分缓存。

将区域设置放在 URL 中，已翻译内容就可以按 URL 缓存。但只有当响应中的其他内容同样与访问者无关时，整个响应才能安全共享。在本示例中，应为个性化的根路径重定向设置 `Vary: Accept-Language, Cookie` 和 `Cache-Control: private`。如果服务器端渲染的语言建议或账户内容因访问者而异，也应将该响应设为私有，或改为在客户端单独渲染该建议。

## 与框架无关的示例

此函数将路由与语言协商分离开来。请参考[语言检测指南](/kb/detect-user-language)，先解析用户已保存的选择，再解析浏览器偏好设置。将得到的支持的语言区域作为 `preferredLocale` 传入，由该函数决定是重定向、直接提供内容，还是建议切换到其他版本。

```ts title="routeLocale.ts"
const LOCALES = ['en', 'de', 'fr', 'es-MX'];
const DEFAULT_LOCALE = 'en';

type Decision =
  | { action: 'serve'; locale: string; suggest?: string }
  | { action: 'redirect'; status: 302; location: string; headers: Record<string, string> }
  | { action: 'not-found' };

export function routeLocale(pathname: string, preferredLocale?: string): Decision {
  const preferred = LOCALES.includes(preferredLocale ?? '') ? preferredLocale : undefined;

  if (pathname === '/') {
    return {
      action: 'redirect',
      status: 302,
      location: `/${preferred ?? DEFAULT_LOCALE}/`,
      headers: { Vary: 'Accept-Language, Cookie', 'Cache-Control': 'private' },
    };
  }

  const segment = pathname.split('/')[1];
  const urlLocale = LOCALES.find((l) => l.toLowerCase() === segment.toLowerCase());
  if (!urlLocale) return { action: 'not-found' };

  return preferred && preferred !== urlLocale
    ? { action: 'serve', locale: urlLocale, suggest: preferred }
    : { action: 'serve', locale: urlLocale };
}
```

| 路径            | 已解析的偏好           | 处理结果           |
| ------------- | ---------------- | -------------- |
| `/`           | 无                | 临时重定向到 `/en/`  |
| `/`           | `de`             | 临时重定向到 `/de/`  |
| `/`           | `fr` (来自已保存的选择)  | 临时重定向到 `/fr/`  |
| `/de/preise`  | 无                | 返回德语页面         |
| `/de/preise`  | `fr`             | 返回德语页面；建议切换到法语 |
| `/en/pricing` | `de`             | 返回英语页面；建议切换到德语 |
| `/pt/precos`  | 无                | 未找到：未配置葡萄牙语    |

该函数仅返回路由决策，不会发送重定向，也不会渲染页面。请在服务端集成中应用其返回的状态码和缓存响应头；并且只有当该页面已在目标语言中发布时，才提供建议版本。这种仅针对根路径的模式只是一种实现方式；只要实际内容位于稳定的本地化 URL 上，其他不带前缀的路径同样可以作为语言中立的入口。

## General Translation 的 gt-next 如何确定语言

在 `gt-next` 中，[`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) 会先检查 URL 中的区域设置，再检查已保存的区域设置 cookie 和 `Accept-Language` 请求头，因此本地化 URL 始终以其对应的语言提供内容。完整的解析顺序和 cookie 名称请参阅[如何检测用户的浏览器语言](/kb/detect-user-language)。

对于公开网站，请在 [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) 中设置 `prefixDefaultLocale: true`。这样英语页面位于 `/en/about`，法语页面位于 `/fr/about`，每个内容 URL 的语言都固定不变，不受访问者偏好影响。`/about` 等不带前缀的路径则作为中立的入口，通过临时重定向跳转到对应语言。[Next.js SEO 指南](/kb/multilingual-nextjs-seo)介绍了如何将此配置与规范网址、元数据和站点地图结合使用。

默认配置下，英语页面不带前缀，`/about` 可能会根据已保存的 cookie 或浏览器请求头进行重定向。显式启用前缀可以避免同一个 URL 既承载英语内容，又充当基于偏好的入口。如需忽略浏览器请求头中的语言偏好，请在 `withGTConfig` 中将 [`ignoreBrowserLocales`](/docs/react/nextjs/config#ignore-browser-locales) 设置为 `true`；URL 和已保存的选择仍会生效。

## 要点总结

* Googlebot 不发送 `Accept-Language` 请求头，且大多从美国 IP 地址抓取，因此根据请求头、cookie 或 IP 选择的内容，其他语言版本可能永远不会被索引。
* 按以下顺序解析语言：URL、用户已保存的选择、`Accept-Language`，最后是默认值；切勿让优先级较低的信号覆盖 URL。
* 在此模式下，仅在中立的根路径上通过临时的 `302` 或 `307` 进行语言协商；`x-default` 为可选项。保持本地化内容的 URL 稳定，并提供指向其他语言版本的链接。
* 稳定的语言 URL 可以共享缓存内容，但因访客而异的重定向、语言建议和账户数据仍需设置相应的缓存控制。
* 在 `gt-next` 中，URL 中的区域设置优先于 cookie 和请求头；`prefixDefaultLocale` 和 `ignoreBrowserLocales` 可进一步收紧这些规则。

## Related questions

- [如何检测用户的浏览器语言？](https://generaltranslation.com/zh/kb/detect-user-language.mdx): 读取浏览器提供的有序语言列表（在 JavaScript 中为 navigator.languages，在服务器端为 Accept-Language 标头），按 q 值排序，再与你支持的语言区域进行匹配，并设置后备语言区域。Safari 和 Chrome 无痕模式只会报告一种语言，而 IP 地理定位只能反映用户所在位置，无法反映其使用的语言。
- [语言切换器最佳实践：用户体验、SEO 与无障碍访问](https://generaltranslation.com/zh/kb/language-switcher.mdx): 用每种语言的本语名称来标注它，例如 Deutsch 或 日本語，不要使用国旗，因为国旗代表的是国家。每个选项都应通过可抓取的链接指向该语言版本的同一页面，用 cookie 记住用户的选择，并且切勿对已本地化的 URL 自动重定向。
- [什么是 hreflang？如何设置？](https://generaltranslation.com/zh/kb/what-is-hreflang.mdx): Hreflang 用于告诉 Google 哪些 URL 是同一页面的不同语言或区域版本，以便在搜索结果中展示正确的版本。你可以通过 HTML link 标签、HTTP Link 响应头或站点地图来添加它，确保所有链接相互指向，并使用有效的代码，例如 en-GB，而不是 en-UK。
- [多语言 SEO：子目录、子域名与 ccTLD 对比](https://generaltranslation.com/zh/kb/multilingual-url-structure.mdx): 如果同一产品共用一个站点和发布流程，请使用 example.com/de/ 这样的子目录。只有 ccTLD 能指明目标国家，子域名便于独立托管；Google 不建议使用 ?lang= 参数，并会将 .io、.ai 等个性化域名视为通用域名。
- [如何为多语言 Next.js 应用设置 SEO？](https://generaltranslation.com/zh/kb/multilingual-nextjs-seo.mdx): 为每种语言提供带前缀的独立 URL，例如 /en/about 和 /es/about，然后为每种已发布的语言提供 html lang、自引用的规范网址、包含 x-default 的 hreflang 替代项、翻译后的 title 和 description，以及站点地图条目。将未发布的版本标记为 noindex。General Translation 的 gt-next 会将每种语言路由到各自的 URL，并通过 getGT 翻译标题。

## Previous and next questions

- Previous question: [语言切换器最佳实践：用户体验、SEO 与无障碍访问](https://generaltranslation.com/zh/kb/language-switcher.mdx)
- Next question: [Locadex 智能体能做什么？](https://generaltranslation.com/zh/kb/what-is-locadex.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/zh/kb/sitemap.md) for every article.
