Haz tu pregunta y obtén un resumen del documento referenciando esta página y el proveedor AI de tu elección
El contenido de esta página ha sido traducido con una IA.
Ver la última versión del contenido original en inglésSi tienes una idea para mejorar esta documentación, no dudes en contribuir enviando una pull request en GitHub.
Enlace de GitHub a la documentaciónCopiar el Markdown del documento a la portapapeles
Hreflang: la guía para SEO multilingüe
Tradujiste tu aplicación. Desplegaste /en, /fr, /es. Y los usuarios franceses siguen llegando a la página en inglés.
Traducir es la mitad fácil. La mitad difícil es decirle a los motores de búsqueda que estas páginas son la misma página en otro idioma, no tres documentos compitiendo entre sí. Eso es lo que hace hreflang, y es donde la mayoría de los sitios multilingües pierden silenciosamente su tráfico.
Qué es realmente hreflang
Una anotación en una página que dice: esta URL tiene versiones equivalentes allá, para esos idiomas.
Copiar el código al portapapeles
Te compra dos cosas: la versión correcta mostrada al usuario correcto, y tus locales consolidados en un clúster en lugar de canibalizarse entre sí como duplicados.
Es importante ser claro sobre lo que no es. No es una redirección — es una sugerencia, y Google puede ignorarla. No es un impulso de clasificación — cambia qué versión se clasifica, no si te clasificas. Y Bing la ignora completamente, confiando en content-language y geo-targeting en su lugar.
Dónde declararlo
Tres ubicaciones, todas válidas. Elige una y mantente ahí — el mismo clúster declarado en dos lugares es cómo los conjuntos se desvinculan.
HTML <head> es la opción habitual. Una advertencia: las etiquetas inyectadas después de la hidratación no son confiables. Si tu framework solo las agrega del lado del cliente, el rastreador puede no verlas nunca.
XML sitemap es mejor a escala. Diez locales en 5 000 páginas significa 50 000 elementos <link> enviados a navegadores sin propósito; en un sitemap cuesta cero bytes a tus páginas.
HTTP Link header es la única opción para archivos que no son HTML como PDFs.
Las reglas
Auto-referencia y reciprocidad
El conjunto en /fr/about debe incluir hreflang="fr" apuntando a /fr/about. Y si /about apunta a /fr/about, /fr/about debe apuntar de vuelta. Google llama a una referencia unidireccional una "no return tag" y la descarta.
En la práctica esto significa que cada página en un cluster envía el conjunto idéntico de links. Generarlos desde una lista de locales compartida no es una conveniencia, es la única forma de mantenerse correcto una vez que tienes más de dos locales.
URLs absolutas, siempre
Copiar el código al portapapeles
La razón vale la pena entender en lugar de memorizar. hreflang es una referencia entre documentos: los motores de búsqueda construyen un clúster identificado por URL, compartido en todas las páginas del mismo. Una ruta relativa solo tiene significado relativo al documento en el que se encuentra, por lo que no puede expresar eso. Tampoco puede cruzar un host — y un alternate muy a menudo lo hace, cuando una locale vive en example.fr o fr.example.com. En un sitemap o un encabezado HTTP no hay un documento base contra el cual resolver de ninguna manera.
Esto tiene una consecuencia directa en el código. getLocalizedUrl("/about", "fr") devuelve /fr/about — relativo adentro, relativo afuera. Para hreflang debes proporcionarle una URL absoluta:
Copiar el código al portapapeles
La única excepción es un framework que resuelve valores relativos por ti antes de renderizar: Next.js expande alternates relativos contra metadataBase. Bien — pero la regla se aplica al HTML emitido, así que verifica con curl, no con el inspector de DevTools.
Códigos de idioma
ISO 639-1 para el idioma, ISO 3166-1 Alpha 2 para la región opcional: fr, fr-CA, pt-BR.
Dos trampas atrapan a casi todo el mundo. Una región sola es inválida — hreflang="ca" es catalán, no Canadá; necesitas en-CA o fr-CA. Y en-UK no existe: el código de país para el Reino Unido es GB, por lo que es en-GB.
Solo agrega una región cuando realmente sirvas contenido diferente en esa región — precios diferentes, avisos legales diferentes. fr y fr-FR en contenido idéntico es ruido.
x-default
Copiar el código al portapapeles
Un concepto que se olvida con más frecuencia y se entiende mal es x-default — menos del 30% de las aplicaciones lo implementan correctamente.
Es el fallback para usuarios cuyo idioma no coincide con nada en tu conjunto. Un hablante holandés en un sitio que ofrece inglés, francés y español no coincide con ninguna entrada; sin x-default, Google elige por ti.
Lo que la gente se equivoca es qué significa. x-default no es "la versión en inglés" y no es "la locale por defecto", aunque generalmente apunte allí. Significa la página para usuarios que este conjunto no cubre. Por eso es legítimo — y a menudo mejor — apuntarlo a una página de selector de idioma o redirección geográfica en lugar de a /en. Si no tienes tal página, tu idioma principal es la respuesta sensata.
Dos cosas que debes tener claras: x-default es una entrada extra en el conjunto, no un reemplazo de la auto-referencial, y como todas las demás entradas debe aparecer idénticamente en cada página del cluster.
La trampa del canonical
Cada página localizada debe ser su propio canonical:
Copiar el código al portapapeles
Apuntar el canonical de cada locale a la versión en inglés en su lugar:
Copiar el código al portapapeles
dice que la página francesa es un duplicado que no debe indexarse, mientras que hreflang dice que es la página a servir a usuarios franceses. Las señales se contradicen, canonical gana, y tus páginas francesas caen fuera del índice.
Canonical es auto-referencial por locale. hreflang describe el cluster.
Elegir una estructura de URL
hreflang anota URLs, así que la estructura viene primero.
Abrir la tabla en una ventana flotante para ver todo el contenido claramente
| Estructura | Ejemplo | Compensación |
|---|---|---|
| Subdirectorios | example.com/fr/ | Un dominio, autoridad compartida — señal geo más débil |
| Subdominios | fr.example.com | Fácil de agregar o eliminar una locale — puede leerse como un sitio separado |
| ccTLDs | example.fr | Señal de país más fuerte — autoridad construida por dominio |
Los subdirectorios son la opción predeterminada correcta para la mayoría de los proyectos. Recurre a los ccTLDs solo cuando realmente operes como negocios de países separados.
La única estructura a evitar: servir diferentes idiomas en la misma URL basándose en Accept-Language o IP. Los rastreadores ven una versión e indexan una versión; todo lo demás es invisible.
Intlayer cubre los tres casos a través derouting.modeyrouting.domains. Consulta dominios personalizados y la referencia de configuración.
Implementación
Escribir manualmente estas etiquetas no sobrevive al contacto con una segunda locale. En su lugar, derívelas de su lista de locales.
Emitir el cluster en cada página
El mismo conjunto en todas partes, canónico por locale, URLs absolutas,
x-defaultincluido.La API de Metadata expone
alternates.languages, ygetMultilingualUrlsconstruye el registro completo desde tus locales configuradas:src/app/[locale]/about/page.tsxCopiar códigoCopiar el código al portapapeles
Configuración completa: Guía de i18n de Next.js 16.
La función
headde la ruta construye los enlaces.localeMapitera sobre tus locales configuradas, por lo que agregar un locale a la configuración lo agrega en todas partes de una sola vez:src/routes/{-$locale}/about.tsxCopiar códigoCopiar el código al portapapeles
headse ejecuta en el servidor, por lo que las etiquetas llegan al HTML inicial. Configuración completa: Guía de i18n de TanStack.O trasládalo todo al sitemap
A escala, mantén las anotaciones fuera de tus páginas por completo.
generateSitemapemite alternativasxhtml:linkpor entrada, leyendo las locales y el modo de routing de tu configuración:src/routes/sitemap[.]xml.tsCopiar códigoCopiar el código al portapapeles
Dos opciones que vale la pena conocer:
xhtmlLinks(defaulttrue) — los alternates se emiten solo donde las URLs de locale realmente difieren. En modono-prefixcada locale comparte una URL, por lo que se omiten a menos querouting.domainsasigne a los locales sus propios nombres de host.entryPerLocale(valor predeterminadofalse) — de forma predeterminada, una entrada<url>lleva todos los alternates. Ambas formas son válidas, pero solo una URL listada como<loc>cuenta como enviada en Search Console; las locales alternativas permanecen descubribles pero sin atribuirse a ningún sitemap. Al activar esto, cada URL localizada obtiene su propia entrada con el conjunto completo de alternates repetidos. Multiplica las entradas por el número de locales, así que ten cuidado con el límite de 50 000 URL / 50 MB y divide en un índice de sitemap si lo superas.
Verifica lo que recibe el crawler
hreflangfalla silenciosamente, así que verifica que no asumir que funciona.Lee la fuente, no el inspector —
curl https://example.com/fr/about | grep hreflangmuestra lo que recibe un crawler; DevTools muestra el DOM después de que JavaScript se ejecutó. Luego sigue cada alternate y confirma que apunta hacia atrás con el conjunto idéntico, y que ninguno de ellos redirige. El informe de International Targeting de Search Console detecta el resto en todo el sitio.Para un crawl específico multilingüe, el Intlayer SEO Scanner verifica etiquetas faltantes, alternates rotos y conflictos canónicos en tus páginas localizadas.
Lista de verificación
- Cada locale tiene una URL distinta y rastreable
- Cada página se auto-referencia, y cada referencia es recíproca
- El mismo conjunto se envía en cada página del cluster
- Todos los valores de
hrefson absolutos en el HTML emitido - Los códigos son ISO 639-1 + ISO 3166-1 Alpha 2 (
en-GB, noen-UK) -
x-defaultestá presente y apunta a dónde deberían ir los usuarios no coincidentes - El canonical es autorreferencial por locale
- Las etiquetas se renderizan del lado del servidor, no se inyectan después de la hidratación
- Declaradas en exactamente un lugar
- Sin redirecciones alternas
Conclusión
hreflang es simple e implacable. Una etiqueta de retorno faltante, una URL relativa, un canonical entre locales, y el cluster se descarta sin ningún error en ningún lugar. Todos esos problemas provienen de escribir las etiquetas a mano.
Derive the set from a single locale list, render it server-side, keep canonical self-referential, and give x-default the thought it deserves. Do that once and correctness stops being something you maintain.
Ir más allá
- SEO e Internacionalización — la imagen más amplia del SEO multilingüe
- SEO e i18n en Next.js —
next-intlvsnext-i18nextvs Intlayer - Guía de i18n de Next.js 16
- Guía de i18n de TanStack Start
- Dominios personalizados por locale
- Referencia de configuración
Comentarios
Aún no hay comentarios. Sé el primero en compartir tus pensamientos.
