A retailer launches German and French versions of a product catalog, adds hreflang tags to the English pages, and waits. Weeks later, German searchers still land on the English pages and the French pages barely surface in Google.fr. Nothing looks broken. View source shows hreflang tags exactly where they should be. The failure is quieter than that: the German pages never point back at the English ones, so Google discards the whole signal and behaves as if hreflang were never there.
That pattern is the most common way hreflang fails, and it explains the attribute’s reputation for fragility. The concept is simple, but the implementation punishes small mistakes hard and fails silently, so the mistake often ships to production and sits there degrading international visibility until someone audits it.
What hreflang actually signals #
A hreflang annotation tells Google two things about a page: the language it is written in, and, optionally, the country it targets. Google uses those signals to serve the right version to the right searcher, so a user in France sees the French page rather than a generic default.
The annotation looks like this in the HTML head:
<link rel="alternate" hreflang="de" href="https://example.com/de/page" />
<link rel="alternate" hreflang="en-us" href="https://example.com/page" />
<link rel="alternate" hreflang="en-gb" href="https://example.com/uk/page" />
<link rel="alternate" hreflang="x-default" href="https://example.com/page" />
The value pairs a language code with an optional region code. So en-us means English content aimed at the United States, and en-gb means English content aimed at the United Kingdom. Language alone, like de, targets German speakers regardless of country.
Google’s documentation is specific about the codes. Language codes follow ISO 639-1, the two-letter set: en for English, de for German, ja for Japanese, zh for Chinese. Region codes, when used, follow ISO 3166-1 Alpha-2: us for the United States, gb for Great Britain, br for Brazil. The region half is optional, but when it appears it has to be a real country code, and the language half is never optional. A value of us on its own is not valid hreflang, because a country code without a language tells Google nothing about what language the page is in.
The reciprocal requirement is the whole game #
If there is one rule that determines whether an implementation works, it is this: every hreflang reference has to be reciprocal. If page A declares that page B is its German version, page B must declare that page A is its English version. Google confirms the relationship only when both sides agree.
Concretely, both pages carry the same block, and each page includes a tag pointing at itself:
<!-- Page A, English at /page -->
<link rel="alternate" hreflang="en" href="https://example.com/page" />
<link rel="alternate" hreflang="de" href="https://example.com/de/page" />
<!-- Page B, German at /de/page -->
<link rel="alternate" hreflang="en" href="https://example.com/page" />
<link rel="alternate" hreflang="de" href="https://example.com/de/page" />
The block is identical on both pages. Each references every version in the cluster, itself included. That self-referencing tag is not optional bookkeeping; Google expects each page to name its own language, and omitting it is one of the more common ways clusters fail validation.
When the return tag is missing, Google sees a one-directional claim it cannot confirm and ignores the hreflang signals for the affected pages entirely. Google’s guidance has been consistent that missing return tags are among the most frequent international SEO problems, and that the durable fix is structural rather than editorial. You do not fix this by hand-checking pages; you fix it by generating both sides of every relationship from one source of truth, so reciprocity is guaranteed by construction rather than by vigilance.
Getting the codes right #
Most code-level mistakes come from a handful of predictable errors:
ukinstead ofgb. The ISO 3166-1 code for the United Kingdom isgb.UKis not a valid country code in that standard at all, so Google does not recognize it. British English isen-gb, neveren-uk.- Inventing codes from the full name.
english-usis wrong because the language code isen, not the word.fr-franceis wrong because the country code isfr, not the country’s name. - Targeting a region that is not a country.
en-eudoes not work, becauseeuis not a country code. Hreflang has no way to target “Europe” as a bloc. Sites either name specific countries or fall back to language-only targeting. - Inconsistent casing. Google’s parser is not case-sensitive, so
EN-USanden-usresolve the same. But mixing them across a site is a symptom of tags being generated in more than one place, which is usually where the real bugs hide.
For reference, these are the codes that cover the common cases:
| Target | Code |
|---|---|
| English, globally | en |
| English, United States | en-us |
| English, United Kingdom | en-gb |
| German, Germany | de-de |
| German, globally | de |
| Spanish, Spain | es-es |
| Spanish, Mexico | es-mx |
| Chinese, simplified | zh-hans |
| Chinese, traditional | zh-hant |
The x-default fallback #
The x-default value names the page Google should serve when none of the other versions match a visitor’s language or location. It is the catch-all for markets the site does not specifically target.
It earns its place in a few situations: as a fallback for unmatched markets, so a visitor from a country the site has no version for lands somewhere sensible; as the annotation for a language-selection landing page; and as the marker for a site’s primary version when several localized versions branch off it.
It is not always necessary. A site that only serves English with country variations may find a language-only en tag already acts as the fallback, and a site whose hreflang covers every plausible visitor has no unmatched market for x-default to catch. It is not required, but including it makes the fallback behavior explicit instead of leaving it to Google’s heuristics, and most sites benefit from that clarity.
Three places to put it #
Hreflang can live in three locations, and Google processes all three the same way. The choice is operational, not a matter of one being stronger.
| Location | When it fits |
|---|---|
| HTML head | The default. Works for any HTML page, visible in view-source, easy to debug. |
| HTTP headers | For non-HTML files like PDFs, or when editing the HTML is impractical. |
| XML sitemap | Scales for large sites; the relationships live in one generated file rather than per-page templates. |
For most sites the HTML head is the path of least resistance, since the tags get emitted by the template and verification is a matter of viewing source. For very large multilingual sites, sitemap hreflang scales better because the entire relationship map is generated in one place. In the sitemap, each URL carries <xhtml:link> alternates for every version, and the reciprocity rule still holds: every URL’s hreflang block has to match every other URL’s block in the cluster. Mixing methods works but adds maintenance surface, so pick one and hold to it.
How to verify it, and what tends to break #
Verification runs on several levels. Google Search Console used to have a dedicated International Targeting report for this, but Google deprecated it in 2022 and removed it. Hreflang problems Google detects during crawling now surface through Search Console’s page indexing reports and the URL Inspection tool rather than a standalone hreflang panel, so anyone still hunting for the old report is looking for something that no longer exists.
Beyond Search Console, third-party validators catch what Google’s own tooling does not surface, crawling the cluster and flagging inconsistencies. The deepest check is still manual: pull the rendered HTML source for a representative page in each language and confirm the tags match what the templates should produce, since some frameworks emit hreflang differently across rendering contexts. Watching the actual results matters too. Searches on Google.de should return German pages and Google.fr French ones; drift from that, even when the tags look correct, signals that something in the cluster has quietly broken.
The failures that recur almost all trace to the same root, hreflang generated ad hoc rather than from one source:
- Missing return tags. The reciprocity break that voids the whole cluster.
- References to URLs that 404 or redirect. A pointer to a non-200 URL is invalid and can take the cluster down with it.
- Mixed codes for the same content. Some pages using
enand othersen-usreads as broken implementation. - Missing self-reference. Each version should point at itself.
- Partial coverage. A category page annotated for ten languages sitting above product pages annotated for none leaves the signal incomplete.
- Robots.txt blocking the referenced URLs. If hreflang on
/en/pagepoints at/de/pagebut robots.txt disallows/de/, Googlebot cannot crawl the German page, cannot confirm the return tag, and treats the cluster as broken. When a language version genuinely should not be indexed, use anoindexmeta tag rather than aDisallowrule, because hreflang depends on the referenced pages staying crawlable.
FAQ #
Does hreflang override Google’s ranking? Will it push my pages up?
No. Hreflang does not raise or lower rankings. It swaps in the correct language or country version of a page that already ranks. It is a targeting signal, not a ranking factor.
Do I still need to worry about the International Targeting report in Search Console?
No. Google deprecated it in 2022 and removed it. Hreflang errors now appear through the page indexing reports and URL Inspection instead.
Can I use hreflang to target all of Europe with one tag?
No. There is no region code for Europe. Region codes must be individual countries under ISO 3166-1 Alpha-2. Target specific countries or use language-only tags.
Is x-default mandatory?
No, it is optional. But it makes the fallback for unmatched markets explicit rather than leaving it to Google’s heuristics, so most multi-market sites include it.
Can I mix hreflang in the HTML head and in the sitemap?
It works, but it doubles the maintenance surface and makes drift more likely. Choose one method and keep the whole site on it.
Hreflang sits one layer down from domain strategy. The choice between country-code domains, subdomains, and subdirectories sets the framework for international presence; hreflang is the operational layer that fills in the language and country relationships inside whichever framework was chosen. That domain-level choice is its own separate decision, not covered here. What makes hreflang work is treating it as infrastructure rather than content: generate it systematically from the translation relationships, verify it in the build, and audit periodically for drift. The implementations that struggle are the ones stitched together page by page as new languages get bolted on. The first fix worth making, before touching any individual tag, is to get every relationship coming from a single source of truth.