# General Translation Knowledge Base: Redirecciones de idioma y SEO: ¿URL, cookie o Accept-Language?
URL: https://generaltranslation.com/es/kb/locale-routing-seo.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Deja que la URL determine el idioma de cada página que quieras indexar, porque Googlebot no envía la cabecera Accept-Language y rastrea sobre todo desde direcciones de EE. UU. Recurre a la preferencia guardada en una cookie y, en su defecto, a Accept-Language solo para decidir adónde redirige una raíz neutral como /, mediante una redirección temporal 302 o 307.
Category: SEO multilingüe
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Las tres señales son útiles, pero responden a preguntas distintas. La URL indica qué versión se solicitó, una cookie indica qué eligió antes este visitante y `Accept-Language` indica cómo tiene configurado el navegador. Solo la URL se ve igual para un rastreador que para tus visitantes, así que debe ser la señal que determine el contenido de cada página.

## ¿Por qué cada idioma necesita su propia URL?

La guía de Google sobre [páginas adaptables a la configuración regional](https://developers.google.com/search/docs/specialty/international/locale-adaptive-pages) explica el problema. Si una página devuelve contenido distinto según el país o idioma que se le atribuye al visitante, es posible que Google no rastree, indexe ni posicione todas las versiones, ya que las direcciones IP predeterminadas de Googlebot parecen estar en EE. UU. y el rastreador envía las solicitudes sin una cabecera `Accept-Language`. Google recomienda usar URL independientes para cada configuración regional, anotadas con [hreflang](/kb/what-is-hreflang).

Googlebot también rastrea desde direcciones IP fuera de EE. UU., pero, según la [guía de sitios multirregionales](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites) de Google, no se varía la ubicación del rastreador para descubrir las variantes de un sitio. Google recomienda usar URL diferentes para cada idioma «en lugar de usar cookies o la configuración del navegador».

Las consecuencias son concretas. Supongamos que `/pricing` sirve la página en alemán a los navegadores que prefieren el alemán y en inglés a todos los demás. Googlebot llega sin `Accept-Language` y sin cookie, recibe la versión en inglés y la indexa. La versión en alemán existe para los usuarios, pero es posible que Google no la descubra ni la indexe.

## Para qué sirve cada señal

| Señal                  | Qué te indica                                                    | Qué envía un rastreador                                           | Úsala para                                             |
| ---------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------ |
| URL (`/de/preise`)     | La versión que se solicitó                                       | La URL que encontró en un enlace, un mapa del sitio o un hreflang | Decidir el contenido: la fuente de verdad indexable    |
| Cookie (`locale=de`)   | El idioma que este visitante eligió anteriormente                | Ninguna de las elecciones de tus visitantes                       | Recordar una elección explícita en visitas posteriores |
| `Accept-Language`      | El idioma de la UI del navegador y las preferencias configuradas | Nada (Googlebot omite la cabecera)                                | Sugerir un idioma en la primera visita                 |
| Geolocalización por IP | Un país aproximado, no un idioma                                 | Una dirección, en su mayoría, de EE. UU.                          | Sugerir una región; rara vez compensa                  |

La cabecera del navegador es menos fiable de lo que parece. La [referencia de `Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language) de MDN la describe como una sugerencia, señala que a menudo escapa al control del usuario (por ejemplo, cuando viaja), indica que Safari y el modo incógnito de Chrome solo incluyen un idioma y afirma que un servidor nunca debe reemplazar una elección de idioma explícita. La IP es aún menos fiable: Google describe el análisis de ubicación por IP como «difícil y, en general, poco fiable». (Consulta [por qué la geolocalización por IP es la señal equivocada](/kb/detect-user-language)).

## Precedencia: URL, luego elección guardada y luego cabecera

Cuando las señales no coinciden, resuélvelas en este orden:

1. **La URL.** Una solicitud a `/de/preise` recibe alemán, desde cualquier navegador y en todos los casos. Así, el contenido de cada URL se mantiene estable, algo de lo que dependen los rastreadores y las cachés.
2. **La elección guardada.** Una cookie o un ajuste de cuenta que el visitante estableció al elegir un idioma. Puede determinar adónde lo redirige la raíz neutral o qué alternativa sugerirle, pero no reescribe una URL localizada en la que haya hecho clic. El [selector de idioma](/kb/language-switcher) debe indicar el idioma que se muestra en ese momento.
3. **La cabecera `Accept-Language`.** Se usa solo cuando no hay una elección guardada, y únicamente para elegir un destino desde la raíz o para ofrecer una sugerencia.
4. **La configuración regional predeterminada.** El contenido alternativo cuando no hay ninguna coincidencia, que también es lo que recibe Googlebot.

Al elegir un idioma en el selector, se navega a la URL de ese idioma, así que la regla 1 ya cubre ese caso.

Cuando una señal de menor prioridad no coincide con la URL, ofrece la otra versión en lugar de cambiar de idioma. Un banner con el texto «Esta página está disponible en Deutsch» respeta tanto el enlace que siguió el visitante como el idioma que probablemente prefiere. Cómo leer estas señales se explica en [cómo detectar el idioma del navegador de un usuario](/kb/detect-user-language).

## ¿Conviene redirigir a los visitantes según el idioma del navegador?

El error habitual es redirigir las URL localizadas según la cabecera o la IP: un visitante con el navegador en francés que abre `/en/pricing` acaba redirigido a `/fr/tarifs`. La guía de Google recomienda a los sitios evitar esta práctica, ya que las redirecciones basadas en el idioma que se presupone al usuario podrían impedir que tanto los usuarios como los motores de búsqueda vean todas las versiones del sitio. Un hablante de francés que quiera la página en inglés no puede acceder a ella, y un rastreador con una dirección de EE. UU. no puede acceder a nada que no esté en inglés.

En su lugar, hay dos alternativas:

* **Enlaces entre versiones.** Google recomienda enlazar cada página con sus versiones en otros idiomas para que los usuarios puedan elegir. Un selector de idioma en cada página también ofrece a los rastreadores una ruta hacia todas las versiones.
* **Una sola redirección, en una raíz neutral.** Una URL raíz como `/` sin contenido propio puede redirigir según la elección guardada o la cabecera, o bien mostrar un selector de idioma. Puedes marcarla como el `x-default` opcional en el grupo hreflang de la página de inicio. Google contempla los selectores de idioma y las páginas de inicio con redirección automática como casos de uso; las páginas internas deben mantener un contenido alternativo relevante para su propio contenido.

### Qué código de estado usar en la raíz

Usa una redirección temporal, `302` o `307`, porque el destino depende de quién haga la solicitud. La [guía sobre redirecciones](https://developers.google.com/search/docs/crawling-indexing/301-redirects) de Google distingue entre un enrutamiento temporal y un cambio permanente de URL: las redirecciones temporales tienden a conservar la URL de origen, mientras que las permanentes, `301` o `308`, son una señal clara de que debe preferirse el destino. Por eso, una redirección permanente desde una raíz independiente del idioma enviaría una señal equivocada cuando el destino no es más que una elección que depende de cada visitante.

## Almacenamiento en caché cuando las respuestas varían

Una caché almacena las respuestas por URL. Si una misma URL devuelve distintos idiomas según una cabecera o una cookie, una CDN puede acabar sirviéndole a un visitante el idioma del anterior. HTTP resuelve esto con la cabecera de respuesta `Vary`:

* **`Vary: Accept-Language`** hace que las cachés usen como clave la URL junto con la cabecera, según la [guía de caché de MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching#vary). Los valores de esta cabecera varían mucho (`en-US,en;q=0.9` y `en-GB,en;q=0.9,de;q=0.8` son claves distintas), por lo que la tasa de aciertos disminuye.
* **`Vary: Cookie`** es aún peor. [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111#section-4.1) solo reutiliza una respuesta almacenada cuando coinciden las cabeceras de solicitud indicadas, y la cabecera `Cookie` incluye todas las cookies, entre ellas las de sesión y analítica. Cuando las respuestas están personalizadas, MDN recomienda usar `Cache-Control: private` en lugar de variar según las cookies.

Incluir la configuración regional en la URL permite almacenar en caché el contenido traducido por URL. Aun así, la respuesta completa solo puede compartirse de forma segura si el resto de su contenido tampoco depende del visitante. En este ejemplo, marca la redirección personalizada de la raíz con `Vary: Accept-Language, Cookie` y `Cache-Control: private`. Si una sugerencia de idioma server-rendered o el contenido de la cuenta varían según el visitante, mantén también esa respuesta como privada o renderiza la sugerencia por separado en el cliente.

## Un ejemplo independiente del framework

Esta función separa el enrutamiento de la negociación del idioma. Primero, resuelve la preferencia guardada y, después, las preferencias del navegador, siguiendo la [guía de detección de idioma](/kb/detect-user-language). Pasa esa configuración regional compatible como `preferredLocale`; la función decide si redirigir, servir la página o sugerir otra versión.

```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 };
}
```

| Ruta          | Preferencia resuelta                    | Decisión                                        |
| ------------- | --------------------------------------- | ----------------------------------------------- |
| `/`           | Ninguna                                 | Redirección temporal a `/en/`                   |
| `/`           | `de`                                    | Redirección temporal a `/de/`                   |
| `/`           | `fr`, a partir de una elección guardada | Redirección temporal a `/fr/`                   |
| `/de/preise`  | Ninguna                                 | Servir en alemán                                |
| `/de/preise`  | `fr`                                    | Servir en alemán; sugerir francés               |
| `/en/pricing` | `de`                                    | Servir en inglés; sugerir alemán                |
| `/pt/precos`  | Ninguna                                 | No encontrado: el portugués no está configurado |

La función devuelve una decisión de enrutamiento; no envía ninguna redirección ni renderiza ninguna página. Aplica sus cabeceras de estado y de caché en la integración del servidor, y ofrece una versión sugerida solo si esa página está publicada en el idioma de destino. Este patrón, limitado a la raíz, es solo una implementación posible; otras rutas sin prefijo también pueden servir como puntos de entrada neutros si el contenido real se encuentra en URL localizadas estables.

## Cómo decide el idioma gt-next de General Translation

En `gt-next`, [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware) comprueba la configuración regional de la URL antes que la cookie de configuración regional guardada y la cabecera `Accept-Language`, de modo que una URL localizada siempre sirve su propio idioma. El orden completo de resolución y el nombre de la cookie se explican en [cómo detectar el idioma del navegador de un usuario](/kb/detect-user-language).

En sitios públicos, establece `prefixDefaultLocale: true` en [`createNextMiddleware`](/docs/react/nextjs/reference/functions/create-next-middleware). Así, el inglés queda en `/en/about`, el francés en `/fr/about` y cada URL de contenido mantiene su idioma con independencia de las preferencias del visitante. Las rutas sin prefijo, como `/about`, funcionan como puntos de entrada neutros con redirecciones temporales. La [guía de SEO para Next.js](/kb/multilingual-nextjs-seo) explica cómo se relaciona esta configuración con las URL canónicas, los metadatos y los mapas del sitio.

Con la configuración predeterminada, el inglés no lleva prefijo y `/about` puede redirigir según la cookie guardada o la cabecera del navegador. Habilitar el prefijo explícitamente evita que esa URL sirva a la vez como contenido en inglés y como punto de entrada basado en preferencias. Para ignorar las preferencias de la cabecera del navegador, establece [`ignoreBrowserLocales`](/docs/react/nextjs/config#ignore-browser-locales) en `true` en `withGTConfig`; la URL y la elección guardada se siguen teniendo en cuenta.

## Conclusiones clave

* Googlebot no envía la cabecera `Accept-Language` y rastrea sobre todo desde direcciones de EE. UU., por lo que el contenido elegido según la cabecera, la cookie o la IP podría no indexarse nunca en otros idiomas.
* Resuelve el idioma en este orden: URL, elección guardada, `Accept-Language` y, por último, el idioma predeterminado; nunca permitas que una señal de menor prioridad reemplace a la URL.
* En este patrón, negocia el idioma solo en una raíz neutral mediante un `302` o `307` temporal; `x-default` es opcional. Mantén estables las URL de contenido localizado y ofrece alternativas.
* Las URL de idioma estables pueden compartir contenido en caché, pero las redirecciones específicas de cada visitante, las sugerencias y los datos de cuenta siguen requiriendo controles de caché adecuados.
* En `gt-next`, la configuración regional de la URL tiene prioridad sobre la cookie y la cabecera; `prefixDefaultLocale` e `ignoreBrowserLocales` hacen las reglas más estrictas.

## Related questions

- [¿Cómo detecto el idioma del navegador de un usuario?](https://generaltranslation.com/es/kb/detect-user-language.mdx): Lee la lista ordenada de idiomas del navegador (navigator.languages en JavaScript o la cabecera Accept-Language en el servidor), ordenada por valor q, y busca la coincidencia con tus configuraciones regionales compatibles, con una configuración regional de respaldo. Safari y el modo incógnito de Chrome solo indican un idioma, y la geolocalización por IP revela la ubicación, no el idioma.
- [Buenas prácticas para el selector de idioma: UX, SEO y accesibilidad](https://generaltranslation.com/es/kb/language-switcher.mdx): Escribe el nombre de cada idioma en ese mismo idioma, como Deutsch o 日本語, y no uses banderas, que representan países. Enlaza cada opción a la misma página en ese idioma mediante un enlace rastreable, guarda la elección en una cookie y nunca redirijas automáticamente una URL localizada.
- [¿Qué es hreflang y cómo se configura?](https://generaltranslation.com/es/kb/what-is-hreflang.mdx): Hreflang le indica a Google qué URL son versiones de la misma página en otros idiomas o regiones, para que pueda mostrar la adecuada en los resultados de búsqueda. Agrégalo con etiquetas link de HTML, cabeceras HTTP Link o un mapa del sitio, asegúrate de que todos los enlaces sean recíprocos y usa códigos válidos como en-GB, no en-UK.
- [Subdirectorio vs. subdominio vs. ccTLD para SEO multilingüe](https://generaltranslation.com/es/kb/multilingual-url-structure.mdx): Usa subdirectorios como example.com/de/ cuando un mismo producto comparte sitio y proceso de lanzamiento. Solo un ccTLD indica un país, los subdominios permiten un alojamiento independiente, Google desaconseja los parámetros ?lang= y trata los dominios personalizados como .io y .ai como genéricos.
- [¿Cómo configuro el SEO de una aplicación de Next.js multilingüe?](https://generaltranslation.com/es/kb/multilingual-nextjs-seo.mdx): Asigna a cada idioma su propia URL con prefijo, como /en/about y /es/about, y luego publica html lang, una URL canónica autorreferente, versiones alternativas hreflang con x-default, un título y una descripción traducidos, y una entrada en el mapa del sitio para cada idioma publicado. Marca con noindex las versiones no publicadas. gt-next de General Translation dirige cada idioma a su propia URL y traduce los títulos con getGT.

## Previous and next questions

- Previous question: [Internacionalización vs. localización (i18n vs. l10n)](https://generaltranslation.com/es/kb/internationalization-vs-localization.mdx)
- Next question: [Subdirectorio vs. subdominio vs. ccTLD para SEO multilingüe](https://generaltranslation.com/es/kb/multilingual-url-structure.mdx)

## Sitemap

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