Overview

Hreflang tells Google which already-indexed URL to show to which language and region. It is not a ranking factor, and it does not translate or improve a page. A site with French, German, and US-English versions of an article needs it so each searcher gets the right one. Settle the URL structure first, because moving locales later is a full site migration.

Pick one locale URL structure before launch

Google documents three workable structures; it does not recommend URL parameters (?loc=de).

StructureExampleWins whenLoses when
ccTLDexample.deClearest geo signal and per-market brandMost expensive; separate domains to register, maintain, and link-build
Subdomainde.example.comSeparate hosting per localeUsers may not read it as a country signal
Subdirectoryexample.com/de/One domain and host; cheapestWeakest geo signal; relies on hreflang

Default to subdirectories on one domain. Use ccTLDs only when the brand or legal entity is separate per country. Googlebot mostly crawls from the US and sends no Accept-Language header, so do not redirect or switch content by IP or browser language; give each locale its own URL, declare variants with hreflang, and offer a language switcher link. Google detects a page’s language from its visible content, not from lang or the URL. Search Console’s International Targeting report was removed in September 2022.

Know what hreflang does and does not fix

  • It helps with the same content translated per language, and with near-identical English for the US, UK, and AU.
  • It does not make a non-English page rank for English queries; body language must match the query.
  • It does nothing for a page that exists in one language, and it does not improve thin or auto-translated content.

Use valid language and region codes

Language is ISO 639-1 and region is ISO 3166-1 alpha-2. A language alone is valid; a region alone is not, and Google ignores it.

<link rel="alternate" hreflang="fr" href="https://example.com/fr/article" />
<link rel="alternate" hreflang="fr-CA" href="https://example.com/fr-ca/article" />

Use en-GB, not en-UK, and zh-CN, not cn. fr serves every French speaker, while fr-CA serves only Canadian ones, so a site with only fr-CA misses France.

Self-reference and pair every alternate

Each page lists every alternate in the cluster, itself included, and every alternate lists it back. Google may ignore annotations not confirmed from the other side. URLs must be fully qualified (https://...).

<!-- Identical on /en/article, /fr/article, and /de/article -->
<link rel="alternate" hreflang="en" href="https://example.com/en/article" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/article" />
<link rel="alternate" hreflang="de" href="https://example.com/de/article" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/article" />

Add x-default for unmatched users

x-default is the fallback for users whose language and region match no entry. Point it at the English version or at a locale-selector page. It is a value, not a language code, and coexists with en or en-US. Google recommends it but does not require it.

Ship hreflang in one place

Google accepts three equal placements: HTML <link> tags, an HTTP header (for non-HTML files such as PDFs), or sitemap <xhtml:link> entries. Pick one source of truth; if you ship two, they must match. Sitemaps scale better on large sites and survive template drift; HTML is easier to debug.

<url>
  <loc>https://example.com/en/article</loc>
  <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/article" />
  <xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/article" />
  <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/en/article" />
</url>

Avoid these hreflang errors

  • Missing return links. The most common bug; validate the full URL set before launch.
  • Canonical pointing at another locale. Each locale page canonicalizes to itself, never to the English version.
  • Alternates that are noindex, redirected, or non-200. The cluster breaks.
  • Forgetting existing pages when adding a locale. Adding /it/ means updating every other locale page.