# General Translation Knowledge Base: Языковые перенаправления и SEO: URL, cookie-файл или Accept-Language?
URL: https://generaltranslation.com/ru/kb/locale-routing-seo.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Язык каждой страницы, которую вы хотите видеть в индексе, должен определять URL, ведь Googlebot не отправляет заголовок Accept-Language и сканирует сайты в основном с американских адресов. Сохранённый в 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 может просканировать, проиндексировать или ранжировать не все её версии: IP-адреса Googlebot по умолчанию определяются как американские, а сам поисковый робот отправляет запросы без заголовка `Accept-Language`. Google рекомендует использовать для каждой локали отдельный URL с разметкой [hreflang](/kb/what-is-hreflang).

Googlebot сканирует сайты и с IP-адресов за пределами США, однако в [руководстве по многорегиональным сайтам](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites) Google указывает, что не меняет местоположение поискового робота, чтобы обнаружить разные варианты сайта. Там рекомендуется использовать для каждого языка свой URL, «а не cookie-файлы или настройки браузера».

Последствия вполне ощутимы. Допустим, `/pricing` отдаёт немецкую версию браузеру, в котором предпочтительным указан немецкий, а всем остальным — английскую. Googlebot приходит без `Accept-Language` и без cookie-файла, получает английскую версию и индексирует её. Немецкая версия доступна пользователям, но Google может так её и не обнаружить и не проиндексировать.

## Для чего подходит каждый сигнал

| Сигнал                    | Что он сообщает                                | Что отправляет поисковый робот                    | Для чего использовать                             |
| ------------------------- | ---------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- |
| URL (`/de/preise`)        | Какая версия была запрошена                    | URL, найденный в ссылке, карте сайта или hreflang | Выбор контента: индексируемый источник истины     |
| 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, не переключайте язык автоматически, а предложите другую версию. Баннер с текстом «Эта страница доступна на языке Deutsch» учитывает и ссылку, по которой перешёл посетитель, и язык, который он, скорее всего, предпочитает. О том, как считывать эти сигналы, рассказывается в статье [как определить язык браузера пользователя](/kb/detect-user-language).

## Стоит ли перенаправлять посетителей по языку браузера?

Распространённая ошибка — перенаправлять с локализованных URL на основе заголовка или IP: посетителя с французским браузером, открывшего `/en/pricing`, перебрасывает на `/fr/tarifs`. Руководство Google советует сайтам этого избегать: перенаправления на основе предполагаемого языка пользователя могут помешать пользователям и поисковым системам увидеть все версии сайта. Франкоязычный пользователь, которому нужна английская страница, не сможет до неё добраться, а поисковый робот с американского адреса не увидит ничего, кроме английской версии.

Вместо этого используйте два приёма:

* **Ссылки между версиями.** Google рекомендует ссылаться с каждой страницы на её версии на других языках, чтобы пользователи могли выбрать нужную. Переключатель языка на каждой странице к тому же открывает поисковым роботам путь к каждой версии.
* **Одно перенаправление — на нейтральном корневом URL.** Корневой URL, например `/`, у которого нет собственного содержимого, может перенаправлять по сохранённому выбору или заголовку либо показывать выбор языка. Его можно указать как необязательный `x-default` в группе hreflang главной страницы. Google приводит выбор языка и главные страницы с автоматическим перенаправлением как допустимые сценарии; внутренние страницы должны сохранять релевантный резервный вариант для собственного содержимого.

### Какой код статуса использовать для корневого URL

Используйте временное перенаправление — `302` или `307`, поскольку адрес назначения зависит от того, кто отправляет запрос. В [руководстве Google по перенаправлениям](https://developers.google.com/search/docs/crawling-indexing/301-redirects) временная маршрутизация разграничивается с постоянным переносом URL: временные перенаправления говорят в пользу сохранения исходного URL, а постоянные перенаправления `301` или `308` служат сильным сигналом предпочесть адрес назначения. Поэтому постоянное перенаправление с языконезависимого корневого адреса подаст неверный сигнал, ведь адрес назначения — лишь выбор, сделанный для конкретного посетителя.

## Кеширование, когда ответы различаются

Кеш хранит ответы по 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-файлы, включая сессионные и аналитические. Для персонализированных ответов MDN рекомендует вместо `Vary` по cookie-файлам использовать `Cache-Control: private`.

Если локаль указана в URL, переведённый контент можно кешировать по 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.

## Как gt-next от General Translation определяет язык

В `gt-next` функция [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) проверяет локаль в URL раньше, чем сохранённый cookie-файл локали и заголовок `Accept-Language`, поэтому локализованный URL всегда отдаёт страницу на своём языке. Полный порядок определения и имя cookie-файла описаны в статье [о том, как определить язык браузера пользователя](/kb/detect-user-language).

Для публичных сайтов задайте `prefixDefaultLocale: true` в [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware). Английская версия будет доступна по адресу `/en/about`, французская — по адресу `/fr/about`, и каждый URL с контентом сохраняет свой язык независимо от предпочтений посетителя. Пути без префикса, например `/about`, служат нейтральными точками входа с временными перенаправлениями. В [руководстве по SEO для Next.js](/kb/multilingual-nextjs-seo) описано, как эта конфигурация связана с каноническими URL, метаданными и картами сайта.

В конфигурации по умолчанию английская версия остаётся без префикса, и `/about` может перенаправляться в зависимости от сохранённого cookie-файла или заголовка браузера. Если явно включить префикс, один и тот же URL не будет одновременно отдавать английский контент и служить точкой входа, зависящей от предпочтений. Чтобы игнорировать языковые предпочтения из заголовков браузера, задайте для [`ignoreBrowserLocales`](/docs/react/nextjs/config#ignore-browser-locales) значение `true` в `withGTConfig`; 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/ru/kb/detect-user-language.mdx): Прочитайте упорядоченный список языков браузера (navigator.languages в JavaScript или заголовок Accept-Language на сервере с сортировкой по q-значению) и сопоставьте его с поддерживаемыми локалями, предусмотрев резервный вариант. Safari и Chrome в режиме инкогнито сообщают только один язык, а геолокация по IP указывает на местоположение, а не на язык.
- [Переключатель языка: лучшие практики UX, SEO и доступности](https://generaltranslation.com/ru/kb/language-switcher.mdx): Называйте каждый язык на нём самом, например Deutsch или 日本語, и не используйте флаги — они обозначают страны. Каждый вариант должен вести на ту же страницу на выбранном языке через индексируемую ссылку. Запоминайте выбор в cookie-файле и никогда не перенаправляйте автоматически с локализованного URL.
- [Что такое hreflang и как его настроить?](https://generaltranslation.com/ru/kb/what-is-hreflang.mdx): Hreflang сообщает Google, какие URL являются языковыми или региональными версиями одной и той же страницы, чтобы в результатах поиска показывалась нужная. Добавьте его с помощью HTML-тегов link, HTTP-заголовков Link или карты сайта, сделайте все ссылки взаимными и используйте корректные коды, например en-GB, а не en-UK.
- [Подкаталог, поддомен или ccTLD для многоязычного SEO](https://generaltranslation.com/ru/kb/multilingual-url-structure.mdx): Используйте подкаталоги вида example.com/de/, если один продукт работает на общем сайте с единым процессом релизов. На страну указывает только ccTLD, поддомены позволяют размещать версии на отдельном хостинге, параметры ?lang= Google не рекомендует, а «имиджевые» домены вроде .io и .ai считает общими.
- [Как настроить SEO для многоязычного приложения Next.js?](https://generaltranslation.com/ru/kb/multilingual-nextjs-seo.mdx): Выделите каждому языку собственный URL с префиксом, например /en/about и /es/about, а затем укажите для каждого опубликованного языка html lang, канонический URL, ссылающийся на саму страницу, альтернативные версии hreflang с x-default, переведённые заголовок и описание, а также запись в карте сайта. Неопубликованные версии пометьте noindex. gt-next от General Translation направляет каждый язык на собственный URL и переводит заголовки с помощью getGT.

## Previous and next questions

- Previous question: [Что такое формат сообщений ICU?](https://generaltranslation.com/ru/kb/icu-message-format.mdx)
- Next question: [Как добавить i18n в приложение Next.js на App Router?](https://generaltranslation.com/ru/kb/add-i18n-to-nextjs.mdx)

## Sitemap

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