Guide : How to Implement Hreflang (Step by Step)

Working code pour tout three hreflang méthodes — HTML head tags, HTTP Lien headers, and XML sitemap annotations — plus the syntax rules, at-scale CMS automation, and the validation workflow to confirmer it en réalité shipped correctement.

Première publication : 2 juil. 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues
1 indice probant sur cette page

Là are exactly three façons to ajouter hreflang — HTML lien tags in the head, HTTP Lien réponse headers (pour non-HTML fichiers comme PDFs), or xhtml:lien entries in an XML sitemap (meilleur at scale). Pick un; Google treats tout three as equivalent and there's aucun benefit to combining les. Every méthode obeys the même two non-negotiable rules: self-reference (chaque page listes itself) and reciprocity (si A points to B, B doit point back to A, or Google ignores the pair). Codes are ISO 639-1 language plus optional ISO 3166-1 alpha-2 region; x-default is the fallback. Generate it tout from a unique source of truth, alors validate the rendered head, pas simplement view-source — in my study of 374 756 domains, over 67% of hreflang setups had au moins un problème.

TL;DR — Three méthodes, pick un: HTML <link> tags in the <head>, HTTP Link: headers (the seulement option pour non-HTML fichiers comme PDFs), or <xhtml:link> entries in an XML sitemap (meilleur at scale — un machine-generated fichier, easiest to QA). Google dit the three are equivalent and there’s aucun benefit to combining les. Every méthode obeys self-reference + reciprocity, uses absolute URLs, and uses ISO 639-1 language + optional ISO 3166-1 alpha-2 region codes. Generate the whole chose from un source of truth — hand-maintained hreflang rots. Alors validate the rendered head, explorer the whole cluster pour reciprocity, and garder re-checking après every URL modifier. Do pas auto-redirect robots d’exploration by geo.

Avant vous écrire a line of code: three decisions

1. Qui méthode. Google is explicit que the choice is à propos de convenience, pas performances — “The three méthodes are equivalent from Google’s perspective and vous peut choisir the méthode that’s the la plupart convenient pour votre site.” My rough rule:

Evidence for this claim Google supports equivalent HTML, HTTP-header, and sitemap methods for declaring localized versions; HTTP headers can be used for non-HTML files such as PDFs. Scope: Google Search hreflang implementation methods. Confidence: high · Verified: Google: Localized versions
  • A handful of URLs, a static or simple site, few locales → HTML <link> tags.
  • Non-HTML assets (PDFs, docs) → HTTP Link: headers (vous have aucun autre option — there’s aucun <head> in a PDF).
  • Nombreux locales, an existing sitemap pipeline, or a headless/JAMstack setup → XML sitemap, generated programmatically.

The Decision Trees lens walks ce as an réel flowchart.

2. Un source of truth. Whatever méthode vous pick, the annotations devrait be generated from un placer — a locale/translation table in votre database, a CMS relationship field, or (pour petit sites) a unique spreadsheet. The unique worst hreflang anti-pattern is hand-maintaining tags par page. The moment un locale’s template drifts, retourner liens go manquant and pairs obtenir dropped.

3. Ne faites pas combine méthodes. Vous peut run tout three; Google dit there’s aucun benefit, and every supplémentaire méthode is un autre surface pour the three copies to disagree with chaque autre.

Evidence for this claim Google requires fully qualified alternate URLs and reciprocal links, and recommends including each page itself in its alternate set. Scope: Google Search hreflang rules shared by all delivery methods. Confidence: high · Verified: Google: Hreflang guidelines

The syntax, verbatim from Google, is:

<link rel="alternate" hreflang="lang_code" href="url_of_page" />

A complet working définir pour a multi-language, multi-region cluster with a fallback:

<link rel="alternate" hreflang="en-us" href="https://example.com/us/" />
<link rel="alternate" hreflang="en-gb" href="https://example.com/uk/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />

Que exact block goes on every page in the cluster (the US page, the UK page, the German page, and the homepage) — chaque un inclut its propre self-referencing line. That’s ce que satisfies reciprocity.

Placement is a hard rule. Google: “The <link> tags doit be à l’intérieur a well-formed <head> section of the HTML.” And leur propre troubleshooting advice: “Si in doubt, paste code from votre rendered page into an HTML validator to garantir que the liens are à l’intérieur the <head> element.”

Ce matters plus que it sounds, parce que of a échec mode la plupart written guides skip: hreflang tags peut be forced out of the <head> and into the <body>, où they’re invalid. In my Pubcon Vegas 2019 deck I flagged ce directement — tags can’t legitimately live in the corps (que voudrait let un site hijack another’s alternates), but a malformed or injected <p> tag, or an iframe, peut prematurely fermer the <head> so que everything après it — notamment votre hreflang — renders à l’intérieur the <body>. The tag is technically présent in source but silently invalid. The fastest façon to debug it is navigateur DOM breakpoints: watch où the tags en réalité land in the rendered DOM, alors walk back to the markup que broke the <head>.

Quand HTML tags faire sense: small-to-medium sites with a manageable number of locales, où the per-page <link> block doesn’t balloon. On a site with dozens of locales, every page carries a grand block of markup — qui is un raison big sites lean on le sitemap méthode.

Don’t mix hreflang with autre alternate attributes on the même <link> element. Google’s guidance is explicit que a <link rel="alternate"> carrying hreflang shouldn’t aussi carry an unrelated alternate attribute comme media in the même tag — si vous besoin les deux a language/region alternate and a media-query alternate pour the même URL, that’s two separate <link> elements, pas un combined un. Mixing les is an facile façon to fin up with a tag Google can’t parse as soit.

Même information, sent in the HTTP réponse au lieu de the HTML. The syntax:

Link: <https://example.com/file.pdf>; rel="alternate"; hreflang="en",
      <https://de-ch.example.com/file.pdf>; rel="alternate"; hreflang="de-ch"

Remarque the angle brackets autour chaque URL, the semicolon-separated attributes, and the comma separating chaque alternate. Every URL in the header is comma-appended.

Quand vous besoin ce: non-HTML resources. A PDF, a .doc, an image served directement — none of les have a <head> to hold <link> tags, so the header is votre seulement façon to attach hreflang to les.

Configuration considerations: vous définir ces headers at le serveur or CDN couche (an Apache Header directive, an Nginx add_header, a Cloudflare/Fastly/CloudFront response-header rule, or votre application’s réponse). Parce que the header is définir by infrastructure plutôt que per-document markup, the reciprocity rule is facile to break ici — the English PDF’s header and the German PDF’s header are usually configuré separately, so it’s on vous to assurez-vous chaque un listes the whole définir, itself inclus. Garder the header generation driven by the même locale table as votre HTML/sitemap approach.

The invariant to hold onto: every alternate réponse carries the identical complet définir, every temps. It’s pas suffisant pour the English PDF’s header to liste the German alternate — the German PDF’s réponse has to carry the exact même définir back (itself plus every autre alternate), on every unique réponse, pas simplement the premier un a robot d’exploration se produit to hit. Treat the header as generated output, pas a one-off vous définir and forget.

At scale, ce is usually the correct appel: the whole cluster lives in un (or a few) machine-generated fichiers, nothing bloats chaque page’s <head>, and — parce que the entier relationship graph is in un placer — it’s by far the easiest méthode to QA. The syntax:

<?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">
  <url>
    <loc>https://www.example.com/english/page.html</loc>
    <xhtml:link rel="alternate" hreflang="de"
                href="https://www.example.de/deutsch/page.html"/>
    <xhtml:link rel="alternate" hreflang="en"
                href="https://www.example.com/english/page.html"/>
  </url>
  <url>
    <loc>https://www.example.de/deutsch/page.html</loc>
    <xhtml:link rel="alternate" hreflang="de"
                href="https://www.example.de/deutsch/page.html"/>
    <xhtml:link rel="alternate" hreflang="en"
                href="https://www.example.com/english/page.html"/>
  </url>
</urlset>

Two choses que trip personnes up in le sitemap méthode:

  1. The namespace declaration is mandatory. Que xmlns:xhtml="http://www.w3.org/1999/xhtml" on the <urlset> element n’est pas optional decoration — omit it and every <xhtml:link> in the fichier is invalid.
  2. Every <url> block doit be self-complete. Notice que les deux <url> blocks ci-dessus liste les deux alternates, notamment leur propre <loc>. Chaque <url> entry carries the complet définir (self-reference + tout alternates). Reciprocity in a sitemap is simplement “every URL’s block lists every URL in the cluster, itself included.”

Two behaviors worth knowing so vous don’t over-engineer the generator: the order of the <xhtml:link> children à l’intérieur a <url> block doesn’t matter to Google — don’t waste temps sorting les — and ceux <xhtml:link> annotations don’t count contre votre sitemap’s 50 000-URL-per-file limite, since they’re children of a <url> entry, pas separate <url> entries themselves.

Generating it programmatically. The whole point of le sitemap méthode is que it’s a byproduct of votre CMS’s propre translation données. Si votre CMS déjà knows que /english/page.html and /deutsch/page.html are translations of chaque autre, votre sitemap generator devrait walk que relationship table and emit the <xhtml:link> blocks at construire temps (or at sitemap-request temps). Do que and hreflang can’t drift out of sync with reality — it’s regenerated from the source of truth every temps, and ajout a locale is a données modifier, pas a hand-edit à travers hundreds of pages.

Pour teams sans dev resources, I construit a lightweight Google Sheets template in my Ahrefs hreflang guide: a Setup tab (pick a par défaut language plus up to four variants), a URLs tab (paste chaque language’s URLs into columns), and a Results tab que auto-generates le sitemap XML block pour vous. It’s the “single source of truth” idea made concrete pour sites aussi petit to justify a réel CMS integration.

The rules que appliquer aucun matter qui méthode vous pick

Ces are method-agnostic and non-negotiable.

  • Self-reference. Every page (or every <url> block, or every header) listes itself. Mueller frames ce as optional-but-good-practice, but skipping it was présent in 18,0% of the domains in my study — automate it and it costs nothing.
  • Reciprocity. “Chaque language version doit liste itself as bien as tout autre language versions.” And the enforcement: “Si two pages don’t les deux point to chaque autre, the tags va be ignored. Ce is so que someone on un autre site can’t arbitrarily créer a tag naming itself as an alternative version of un of votre pages.” Un manquant retourner lien drops the pair.
  • Absolute, fully-qualified URLs. “Alternate URLs doit be fully-qualified, notamment the transport méthode (http/https)” — so https://example.com/foo, jamais //example.com/foo or /foo. And l’URL doit match the exact formulaire Google indexes: protocol, www, trailing slash, and cas tout have to line up, or the return-link match fails.

Getting le codes correct

“The premier code of the hreflang attribute is the language code (in ISO 639-1 format) followed by an optional second code que represents the region code (in ISO 3166-1 Alpha 2 format).” Combine les with a dash: en-US. Vous pouvez target a language on its propre (es = Spanish everywhere), but vous ne peut pas target a region on its propre — there’s toujours a language premier.

The mistakes I voir la plupart souvent (and saw à travers the study dataset):

  • en-UK au lieu de en-GBuk is Ukrainian, the UK’s region code is gb.
  • jp au lieu de ja pour Japanese, cn au lieu de zh pour Chinese.
  • Three-letter codes (ger, eng) où two-letter ISO 639-1 is requis.
  • En utilisant EU, UN, or UK as region codes — none are valid ISO 3166-1 alpha-2 targets.

x-default is the reserved valeur pour the fallback — a language selector or an auto-redirecting home page que sert utilisateurs who match none of votre explicit locales:

<link rel="alternate" href="https://example.com/" hreflang="x-default" />

It’s pas mandatory. It was the unique most-missing element in my study (56,3% of domains omitted it), but a manquant x-default doesn’t break the cluster the façon a manquant reciprocal tag fait — Google simplement falls back to its propre language/region detection pour unmatched utilisateurs. Ajouter it anyway; it’s a checklist item, pas a cluster-breaker. (There’s a dedicated x-default subtopic in ce cluster.)

Implementing at scale — CMS and platform approaches

WordPress. Yoast SEO fait pas generate hreflang on its propre. To en réalité output tags vous besoin a multilingual plugin — WPML or Polylang. WPML auto-generates hreflang pour every page que has translations, adds an x-default pointing at votre default-language version, and by par défaut injects the annotations into the XML sitemap; there’s a setting sous WPML → Languages → SEO Options (“Display alternative languages in the HEAD section”) si vous vouloir head-tag output au lieu de, or en outre to, le sitemap. (Voir WPML’s docs on en utilisant it with Yoast.)

Shopify. Shopify Markets automatically generates mutually-linked hreflang tags une fois you’ve configuré markets/languages and le contenu is publié and lié in navigation — qui removes the la plupart courant échec mode (manquant reciprocal liens) by design. The catch: Markets’ automatic tags are souvent language-only (fr, de) plutôt que language-region (fr-FR, fr-CA), qui under-serves brands que besoin regional specificity — French pour France vs. Canada vs. Belgium. Pour que vous drop to manual <link> tags in theme.liquid (Shopify’s propre theme hreflang docs montrer the pattern) or an app — and faites attention, parce que mixing automatic Markets tags with manual/app tags is a documented source of conflicts. Pick un.

Custom / headless / enterprise. Generate the annotations from votre translation-relationship table at construire or serve temps, as décrit in le sitemap section ci-dessus. Ce is the même “single source of truth” principle — hreflang becomes a computed output of données votre CMS déjà holds, pas a hand-maintained artifact que peut rot.

Implementation mistakes que break the cluster

Auto-redirecting by geo/IP. Ce is a separate échec from syntax errors and it’s worse. Si vous redirection visitors — and surtout robots d’exploration — to a version fondé on perceived emplacement, vous pouvez de-index entier regional clusters, parce que Googlebot mostly crawls from the US. As I put it in que Pubcon deck: a geo-redirect “voudrait redirection moteur de recherches to où ils explorer from. Google pour instance mostly crawls from the US so we voudrait effectively de-index tout geo pages.” There’s a regulatory angle aussi — potential exposure sous EU anti-geoblocking rules. The correct pattern is redirection utilisateurs, jamais robots d’exploration: detect and (optionally) redirection human visitors, but toujours let bots reach every URL variant directement, with hreflang doing the réel routing signal. Google’s propre multi-regional guidance recommends a non-intrusive suggestion banner over automatic redirection pour exactly ce raison.

Pointing hreflang at non-canonical, redirigé, or noindexed URLs. Si a locale’s URL changements and the redirection goes in but the hreflang encore points at the old URL, votre cluster now références a 301 or a 404 (16,9% of domains in my study referenced broken/redirigé pages). Similarly, chaque variant devrait canonicalize to itself; pointing hreflang at une URL that’s canonicalized away, or at a noindexed page, breaks the retourner lien (8,0% pointed at non-URL canoniques).

Inconsistent URL formats. The hreflang URL and the indexé URL have to be byte-identical in formulaire — trailing slash, www, protocol, and cas tout inclus. A mismatch ici is a silent reciprocity échec.

Validating votre implementation après launch

View rendered source, pas simplement raw source. Si votre hreflang is injected by client-side JavaScript, curl or Ctrl+U (“View Page Source”) va montrer nothing — but the tags exist in the rendered DOM. Confirmer with GSC Inspection d’URL → “Tester Live URL” → “View Testé Page,” or a rendering-capable robot d’exploration. Ce is the unique la plupart courant raison someone dit “my tags are missing” quand they’re en réalité fine (or the reverse — présent in source but broken parce que ils render in the <body>).

Explorer the whole cluster, don’t spot-check un URL. Reciprocity is a relationship entre pages, so checking un page indique vous almost nothing. Run a robot d’exploration à travers the whole cluster — Screaming Frog’s hreflang audit or Ahrefs Site Audit — to catch manquant retourner tags, non-canonical targets, and broken références at scale. Ahrefs Site Audit’s Hreflangs tab draws the cluster as a graph with broken liens in red, qui is far easier to raison à propos de que a CSV.

Vérifier réel SERPs with &hl= and &gl=. Append the host-language (&hl=) and geolocation (&gl=) parameters to a Recherche Google URL to preview ce que a donné locale’s results en réalité regarder comme, plutôt que guessing from votre propre emplacement.

Validation n’est pas a one-time step. Nouveau locales, URL/redirection changements, and dev/staging config bleeding into production are the recurring causes of post-launch breakage. Construire hreflang checks into regression tests and recurring crawls, pas simplement a launch-day QA réussir. My framing from the talks: “quelconque number of choses peut break from masking to choses carrying over from dev/tester/staging environments.”

Myths worth retiring

  • “Sitemaps process faster than HTML tags.” Faux. Les deux are resolved at explorer temps — I’ve debunked ce directement. Le sitemap’s advantage is maintainability and QA, pas speed.
  • “Yandex doesn’t support hreflang in sitemaps.” Faux — Yandex’s propre documentation confirms sitemap hreflang prise en charge.
  • “Use all three methods for extra signal.” Aucun benefit per Google, and it simplement multiplies inconsistency risk.
  • “A missing x-default breaks the cluster.” Faux — it’s optional; Google falls back to its propre detection.
  • “Wrong hreflang gets you penalized.” Faux — it’s a hint, pas a directive. Broken hreflang is ignored, pas penalized.

Où to go suivant

Ce article is the how-to depth sous the hreflang hub — the hub covers ce que hreflang is, pourquoi reciprocity matters, and ce que Bing fait à la place. The x-default subtopic goes deep on the fallback valeur. Pour the strategy ce implements, voir the International SEO pillar — hreflang is the technical couche, pas a substitute pour genuine localization.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.