SEO//12 min de lectura

hreflang Tags: La guía completa de implementación para SEO multilingüe

Eray Gündoğmuş
Compartir

hreflang Tags: La guía completa de implementación para SEO multilingüe

Si tienes un sitio web en más de un idioma o te diriges a usuarios en distintos países, las hreflang tags son la señal técnica de SEO más importante que puedes implementar. Hazlo bien y Google mostrará la versión correcta del idioma a cada usuario. Hazlo mal y te enfrentarás a penalizaciones por contenido duplicado, canibalización entre tus propias páginas y caída de rankings en las regiones a las que intentas servir activamente.

Esta guía recorre el panorama completo: qué hacen realmente las hreflang tags, las tres formas de implementarlas, cada patrón común que encontrarás en la práctica, fragmentos de código para frameworks modernos de JavaScript, los errores que rompen implementaciones silenciosamente y las herramientas para validar todo antes del lanzamiento.


TL;DR / Puntos clave

  • hreflang es un atributo HTML que le dice a Google qué variante de idioma o regional mostrar para una audiencia determinada. Bing no utiliza hreflang — se basa en otras señales como las cabeceras HTTP Content-Language y datos de geolocalización.
  • Cada página de un conjunto hreflang debe incluir una tag de autorreferencia que apunte a sí misma, además de tags que apunten a todas las demás variantes.
  • Las relaciones hreflang deben ser bidireccionales: si la página A declara a la página B como variante, la página B también debe declarar a la página A. Las declaraciones unidireccionales son ignoradas.
  • El valor x-default es un designador de respaldo, no un código de idioma. Úsalo para páginas que actúan como selector de idioma, puerta de enlace de redirección o capturador para locales no coincidentes.
  • hreflang puede implementarse en tres lugares: tags HTML <link> en <head>, cabeceras de respuesta HTTP o sitemaps XML. Los tres son igualmente válidos para Google.

¿Qué son las hreflang tags?

Las hreflang tags son anotaciones HTML que indican a los motores de búsqueda qué versión de una página está destinada a usuarios que hablan un idioma particular o se encuentran en un país particular. El atributo fue introducido por Google en 2011 y está documentado en Google Search Central bajo el nombre "Informa a Google sobre las versiones localizadas de tu página".

El problema central que hreflang resuelve es la desambiguación. Supón que tu sitio existe en inglés en /en/, en alemán en /de/ y en alemán para usuarios austriacos en /de-at/. Sin hreflang, el rastreador ve tres páginas con contenido sustancialmente similar y debe adivinar cuál clasificar para cada audiencia. A menudo adivina incorrectamente. Con hreflang, declaras la relación explícitamente.

Cómo usa Google hreflang

Cuando Google encuentra anotaciones hreflang, las usa para agrupar páginas en conjuntos de equivalencia. Dentro de un conjunto, Google selecciona la variante más apropiada para cada consulta según el idioma de interfaz del usuario y, en menor medida, su ubicación. hreflang no garantiza el ranking — solo indica qué página preferir para una audiencia una vez que la página ya tiene señales de ranking.

hreflang también resuelve el contenido duplicado. Cuando Google ve dos URLs con contenido idéntico o casi idéntico y enlaces hreflang válidos conectándolas, las trata como variantes regionales intencionales en lugar de duplicados accidentales.

Google vs Bing: Una distinción importante

hreflang es una función de Google (y Yandex). Bing explícitamente no usa hreflang tags. Según las Directrices para Webmasters de Bing, Bing usa las siguientes señales:

  • La cabecera de respuesta HTTP Content-Language
  • El atributo lang en el elemento <html>
  • La ubicación geográfica del servidor
  • El dominio de nivel superior con código de país (ccTLD), cuando aplica
  • El contenido de la página en sí

Un sitio multilingüe bien construido implementa ambas cosas: hreflang para Google y el atributo lang en <html> más cabeceras Content-Language para Bing. Los dos enfoques no entran en conflicto.


Sintaxis y colocación

Formato de códigos de idioma y región

Los valores de hreflang siguen el formato definido por IETF BCP 47:

  • Solo idioma: en, de, fr, ja, zh-Hans, zh-Hant
  • Idioma más región: en-US, en-GB, de-AT, fr-CA, pt-BR
  • Valor especial: x-default

Los códigos de idioma son códigos de dos letras ISO 639-1. Los códigos de región son códigos de dos letras ISO 3166-1 alpha-2. El separador es un guion, no un guion bajo. Un error común es escribir en_US (guion bajo) en lugar de en-US (guion) — Google ignora completamente la variante con guion bajo.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Product Page</title>

  <!-- Tag de autorreferencia (obligatorio) -->
  <link rel="alternate" hreflang="en" href="https://example.com/en/product/" />

  <!-- Otras variantes de idioma -->
  <link rel="alternate" hreflang="de" href="https://example.com/de/product/" />
  <link rel="alternate" hreflang="fr" href="https://example.com/fr/product/" />
  <link rel="alternate" hreflang="de-AT" href="https://example.com/de-at/product/" />

  <!-- Respaldo para locales no coincidentes -->
  <link rel="alternate" hreflang="x-default" href="https://example.com/product/" />
</head>
<body>
  <!-- contenido de la página -->
</body>
</html>

Cada variante de idioma de esta página debe incluir exactamente el mismo conjunto de tags <link>. La página en alemán en /de/product/ también debe tener los cinco elementos <link>, incluido el que apunta a /en/product/.

Método 2: Cabeceras de respuesta HTTP

Para recursos no HTML — los PDFs son el caso más común — el servidor web emite hreflang como parte de la cabecera de respuesta HTTP Link.

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Link: <https://example.com/en/product/>; rel="alternate"; hreflang="en",
      <https://example.com/de/product/>; rel="alternate"; hreflang="de",
      <https://example.com/fr/product/>; rel="alternate"; hreflang="fr",
      <https://example.com/product/>; rel="alternate"; hreflang="x-default"

En una configuración de Nginx:

location /en/product/ {
    add_header Link '<https://example.com/en/product/>; rel="alternate"; hreflang="en", <https://example.com/de/product/>; rel="alternate"; hreflang="de", <https://example.com/fr/product/>; rel="alternate"; hreflang="fr"';
}

Método 3: Sitemap XML

Para sitios grandes con miles de URLs, los sitemaps XML ofrecen una alternativa centralizada a través del elemento xhtml:link.

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">

  <!-- Variante en inglés -->
  <url>
    <loc>https://example.com/en/product/</loc>
    <xhtml:link rel="alternate" hreflang="en"       href="https://example.com/en/product/" />
    <xhtml:link rel="alternate" hreflang="de"       href="https://example.com/de/product/" />
    <xhtml:link rel="alternate" hreflang="fr"       href="https://example.com/fr/product/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/product/" />
  </url>

  <!-- Variante en alemán — mismo conjunto de elementos xhtml:link -->
  <url>
    <loc>https://example.com/de/product/</loc>
    <xhtml:link rel="alternate" hreflang="en"       href="https://example.com/en/product/" />
    <xhtml:link rel="alternate" hreflang="de"       href="https://example.com/de/product/" />
    <xhtml:link rel="alternate" hreflang="fr"       href="https://example.com/fr/product/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/product/" />
  </url>

  <!-- Variante en francés — mismo conjunto de elementos xhtml:link -->
  <url>
    <loc>https://example.com/fr/product/</loc>
    <xhtml:link rel="alternate" hreflang="en"       href="https://example.com/en/product/" />
    <xhtml:link rel="alternate" hreflang="de"       href="https://example.com/de/product/" />
    <xhtml:link rel="alternate" hreflang="fr"       href="https://example.com/fr/product/" />
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/product/" />
  </url>

</urlset>

Patrones comunes de hreflang

Mismo idioma, diferentes regiones

Usa tags de idioma más región cuando tengas contenido significativamente diferente para el mismo idioma en distintos países.

<!-- Inglés de Estados Unidos -->
<link rel="alternate" hreflang="en-US" href="https://example.com/us/checkout/" />

<!-- Inglés del Reino Unido -->
<link rel="alternate" hreflang="en-GB" href="https://example.com/uk/checkout/" />

<!-- Inglés de Australia -->
<link rel="alternate" hreflang="en-AU" href="https://example.com/au/checkout/" />

<!-- Respaldo para todos los demás angloparlantes -->
<link rel="alternate" hreflang="en" href="https://example.com/en/checkout/" />

La tag x-default

x-default designa la página que Google debe mostrar a usuarios cuyo idioma no coincide con ninguna de tus variantes declaradas.

<!-- Variantes específicas de idioma -->
<link rel="alternate" hreflang="en" href="https://example.com/en/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/" />
<link rel="alternate" hreflang="ja" href="https://example.com/ja/" />

<!-- x-default: se muestra cuando ninguna variante coincide con el idioma del usuario -->
<link rel="alternate" hreflang="x-default" href="https://example.com/" />

x-default no es un código de idioma. No lo uses como sinónimo de inglés.

Requisito de autorreferencia

Cada página en un conjunto hreflang debe incluir una tag que apunte a sí misma. Esto no es opcional.

<!-- En la página /de/, la autorreferencia es la tag hreflang="de" -->
<link rel="alternate" hreflang="de" href="https://example.com/de/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about/" />

Requisito de enlaces de retorno (bidireccionalidad)

Si la página A declara a la página B como alternativa, la página B debe declarar a la página A como alternativa. Este es el fallo silencioso más común en las implementaciones de hreflang.


Implementación en frameworks populares

Next.js (App Router)

// app/[locale]/product/[slug]/page.tsx
import type { Metadata } from 'next';

type Props = {
  params: { locale: string; slug: string };
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { locale, slug } = params;
  const baseUrl = 'https://example.com';

  return {
    alternates: {
      canonical: `${baseUrl}/${locale}/product/${slug}/`,
      languages: {
        'en': `${baseUrl}/en/product/${slug}/`,
        'de': `${baseUrl}/de/product/${slug}/`,
        'fr': `${baseUrl}/fr/product/${slug}/`,
        'x-default': `${baseUrl}/en/product/${slug}/`,
      },
    },
  };
}

Nuxt 3

// composables/useHreflang.ts
export function useHreflang(slug: string) {
  const baseUrl = 'https://example.com';
  const locales = ['en', 'de', 'fr'];

  useHead({
    link: [
      ...locales.map((locale) => ({
        rel: 'alternate' as const,
        hreflang: locale,
        href: `${baseUrl}/${locale}/${slug}/`,
      })),
      {
        rel: 'alternate' as const,
        hreflang: 'x-default',
        href: `${baseUrl}/en/${slug}/`,
      },
    ],
  });
}

Astro

---
// src/layouts/BaseLayout.astro
interface Props {
  hreflangLinks: Array<{ hreflang: string; href: string }>;
}
const { hreflangLinks } = Astro.props;
---
<html>
  <head>
    {hreflangLinks.map((link) => (
      <link rel="alternate" hreflang={link.hreflang} href={link.href} />
    ))}
  </head>
  <body>
    <slot />
  </body>
</html>
---
// src/pages/[locale]/[slug].astro
export async function getStaticPaths() {
  const baseUrl = 'https://example.com';
  const locales = ['en', 'de', 'fr'];
  const slugs = ['about', 'pricing', 'contact'];

  return locales.flatMap((locale) =>
    slugs.map((slug) => ({
      params: { locale, slug },
      props: {
        hreflangLinks: [
          ...locales.map((l) => ({
            hreflang: l,
            href: `${baseUrl}/${l}/${slug}/`,
          })),
          { hreflang: 'x-default', href: `${baseUrl}/en/${slug}/` },
        ],
      },
    }))
  );
}
const { hreflangLinks } = Astro.props;
---
<BaseLayout hreflangLinks={hreflangLinks}>
  <!-- contenido de la página -->
</BaseLayout>

TanStack Start

// routes/$locale.product.$slug.tsx
import { createFileRoute } from '@tanstack/start';

const locales = ['en', 'de', 'fr'] as const;
const baseUrl = 'https://example.com';

export const Route = createFileRoute('/$locale/product/$slug')({
  head: ({ params }) => {
    const { locale, slug } = params;

    const hreflangLinks = [
      ...locales.map((l) => ({
        tag: 'link' as const,
        attrs: {
          rel: 'alternate',
          hreflang: l,
          href: `${baseUrl}/${l}/product/${slug}/`,
        },
      })),
      {
        tag: 'link' as const,
        attrs: {
          rel: 'alternate',
          hreflang: 'x-default',
          href: `${baseUrl}/en/product/${slug}/`,
        },
      },
    ];

    return { links: hreflangLinks };
  },
  component: ProductPage,
});

function ProductPage() {
  const { locale, slug } = Route.useParams();
  return <div>{/* contenido de la página */}</div>;
}

Frameworks con enrutamiento i18n integrado, como Next.js con @formatjs/intl o plataformas como Better i18n que gestionan la entrega de traducciones, pueden generar o automatizar el output hreflang en el build o en tiempo de ejecución.


Errores comunes

Autorreferencia faltante

<!-- INCORRECTO: la página /de/ no se referencia a sí misma -->
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about/" />

<!-- CORRECTO: la página /de/ incluye su propia autorreferencia -->
<link rel="alternate" hreflang="de" href="https://example.com/de/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/about/" />

Códigos de idioma incorrectos

  • en_US en lugar de en-US (guion bajo vs guion)
  • zh cuando se quiere decir zh-Hans (Simplificado) o zh-Hant (Tradicional)
  • pt cuando se necesita distinguir pt-PT (Portugal) de pt-BR (Brasil)
  • Códigos ISO 639-2 de tres letras (zho, deu) en lugar de ISO 639-1 de dos letras (zh, de)

Canonical tags incorrectas

<!-- INCORRECTO: canonical y autorreferencia hreflang no coinciden -->
<link rel="canonical" href="https://example.com/product/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/product/" />

<!-- CORRECTO: canonical y autorreferencia apuntan a la misma URL -->
<link rel="canonical" href="https://example.com/en/product/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/product/" />

Usar hreflang para páginas no equivalentes

hreflang declara que dos páginas son traducciones o variantes regionales entre sí. Usarlo para conectar páginas no equivalentes hace que Google malinterprete la estructura del sitio.


Herramientas de validación

Google Search Console

El informe de Cobertura muestra errores de hreflang directamente:

  • Return tag missing: Google encontró un enlace hreflang pero la página de destino no enlaza de vuelta.
  • No hreflang tag: Una página está referenciada como alternativa pero no contiene hreflang tags.

Herramientas de verificación hreflang

  • hreflang.org Tag Checker: Verifica todas las anotaciones y return links para una URL.
  • Herramienta de prueba de Aleyda Solis: Gratuita, útil para verificar URLs individuales.
  • Generador de hreflang Tags de Merkle: Genera markup HTML, de cabecera HTTP o de sitemap correcto.

Screaming Frog SEO Spider

Para auditorías a gran escala, rastrea todo el sitio y señala return links faltantes, códigos de idioma inválidos y canonicals no coincidentes en todo el crawl.


FAQ

¿hreflang afecta directamente los rankings?

No. hreflang previene problemas de contenido duplicado y garantiza que la página correcta compita para la audiencia correcta, lo que puede mejorar indirectamente las tasas de clics y las señales de engagement.

¿Puedo usar hreflang en un sitio con un solo idioma pero múltiples versiones por país?

Sí. Si tienes páginas distintas con contenido genuinamente diferente (precios, textos legales, disponibilidad de productos), usar tags en-US, en-GB y en-AU es correcto. Si el contenido es idéntico, considera la segmentación geográfica vía Google Search Console.

¿Cuánto tiempo tarda Google en procesar los cambios de hreflang?

Días o semanas. Enviar un sitemap XML actualizado a través de Search Console acelera el proceso.

¿Debo usar hreflang en cada página o solo en las páginas clave?

En cada página que tenga una variante traducida. La implementación parcial causa inconsistencias impredecibles.

¿Qué pasa si tengo errores de hreflang y no hago nada?

Google trata las anotaciones erróneas como si no existieran y recurre a sus propias señales. Normalmente la variante regional incorrecta aparece en los resultados de búsqueda.


Conclusión

Las hreflang tags requieren consistencia en cada página, variante y método elegido. Un solo return link roto o una autorreferencia faltante puede invalidar silenciosamente parte del targeting internacional.

La lista de verificación: códigos BCP 47 con guiones, autorreferencia siempre, enlaces bidireccionales siempre, alinear canonical tags, validar con Screaming Frog y Search Console antes del lanzamiento.

Para proyectos nuevos, elige un framework que genere hreflang automáticamente como parte de su enrutamiento i18n. Para sitios existentes, el sitemap XML es la adaptación de menor fricción.


Referencias

Comments

Loading comments...