hreflang Done Right: International Targeting Without the Errors
A SaaS company launched localized sites for the UK, Australia, and Canada, each with carefully translated pricing in local currency. Three months later, support tickets started: Australian visitors were landing on the UK pricing page and seeing pounds. The hreflang markup was present on every page, validated clean in a generic HTML checker, and completely non-functional. The Australian page pointed to the UK and Canadian variants. The UK page pointed only to Canada and Australia. Because the return tags did not form a complete loop, Google discarded the entire cluster and fell back to serving whatever it had decided was canonical — the UK page. No error appeared anywhere a casual check would find it.
This is the defining characteristic of hreflang: it fails silently. There is no broken-link 404, no console warning, no visible symptom on the page. Google encounters a malformed cluster, decides it cannot trust the relationships, and simply ignores the annotation — reverting to its own judgment about which page to serve to which user. The wrong-region page that results looks intentional. You only discover the failure through traffic patterns, support complaints, or a tool that actually validates the cluster as a graph rather than checking each tag in isolation.
Reciprocity Is the Whole Game
hreflang is not a set of one-way pointers. It is a bidirectional contract, and the contract is void unless both sides sign it.
The rule: if page A includes <link rel="alternate" hreflang="en-au" href="B">, then page B must include a return tag pointing back to A. Every page in a language cluster must reference every other page in the cluster, including itself with a self-referencing hreflang. A cluster of four regional variants therefore contains four annotations on each page (one self-reference plus three siblings), and all sixteen pointers must be present and consistent. Miss one return tag and Google treats the relationship between those two pages as unverified.
The reason for this strictness is that hreflang is a trust signal Google cannot independently verify. Anyone can claim “this German page is the alternate of my English page” — the return tag is how Google confirms the German page agrees. Without mutual declaration, hreflang would be an open invitation to hijack another site’s signals by claiming to be its alternate. The reciprocity requirement closes that hole, at the cost of making the markup unforgiving.
In practice, reciprocity breaks for mundane reasons. A new regional variant gets added and the existing pages’ clusters are never updated to include it, so the new page points outward but nothing points back. A page gets a trailing-slash change or moves to HTTPS and the hreflang href values still use the old URL, so the return tag technically exists but points at a URL that now redirects — and hreflang must point at the final, canonical, 200-status URL, never a redirecting or non-canonical one. A CMS template emits the cluster for the current page but forgets the self-reference. Each of these produces a partial graph, and a partial graph is a discarded graph.
The correct mental model is a fully connected, undirected graph where every node references every node. Validate it as a graph: for each page, confirm that every page it points to also points back, and that it points to itself. Tag-by-tag validation will pass a broken cluster because each individual tag is well-formed — the defect is in the relationships between tags, which only graph-level analysis exposes.
Language and Region Codes Have Strict Rules
The hreflang attribute value is not free text. It is a language code, optionally followed by a region code, and both halves draw from specific standards. Getting them wrong is the second most common failure.
The language code must be a valid ISO 639-1 two-letter code: en, de, fr, es, pt. The optional region code must be a valid ISO 3166-1 Alpha-2 country code: GB, US, AU, DE. The two are joined by a hyphen: en-GB, pt-BR, es-MX. Case is technically insensitive to Google but convention is lowercase-language, uppercase-region.
The errors cluster around a few predictable mistakes:
Using a country code as a language code. hreflang="uk" is invalid — uk is the ISO 639-1 code for Ukrainian, not a code for the United Kingdom. The UK is gb. A site targeting British English is en-GB, and en-uk is simply not a thing Google recognizes. This single error — uk for the UK — appears constantly and silently invalidates the annotation.
Inventing region-only codes. There is no hreflang="eu" for “Europe” or hreflang="latam" for Latin America. hreflang targets a language, optionally narrowed to a country. There is no continental or economic-bloc targeting. If you want to target Spanish speakers across Latin America without a specific country, use es alone (language only) — not an invented region.
Region without a valid language. hreflang="DE" (region only, no language) is invalid. hreflang is fundamentally language-first; the region merely narrows it. You cannot target a country irrespective of language through hreflang.
Mismatched language and content. This one passes validation but fails in spirit: declaring hreflang="de-DE" on a page whose visible content is in English. The code is syntactically valid, so no validator complains, but Google can detect the language mismatch and may distrust the annotation. The declared language should match the page’s actual content language.
When the language genuinely has script variants, BCP 47 allows a script subtag — zh-Hans for Simplified Chinese, zh-Hant for Traditional — and Google supports these. But add a script subtag only when you actually serve script-differentiated content; otherwise zh suffices.
x-default and the Return-Tag Failures
x-default is the entry that tells Google what to serve when no language-region combination matches the user. It is not a real locale — it is the fallback rule for everyone your other annotations do not cover.
Use it for the homepage of a site with a language selector, for a global or international landing page, or for your most broadly applicable variant. A user in a market you do not target — someone in Japan visiting a site that only declares en-US, en-GB, and de-DE — gets the x-default page. Without x-default, Google falls back to its own canonical choice, which is often the wrong region.
The frequent x-default mistakes: omitting it entirely (so non-targeted users get a guess), pointing it at a region-specific page like /us/ (which makes US the de facto default and defeats the neutrality x-default is meant to provide), and forgetting that x-default participates in reciprocity too — the page you designate as x-default must include the x-default annotation in its own cluster, and every page in the cluster should reference the same x-default URL.
Return-tag errors — Search Console’s old International Targeting report labeled them “no return tags” — are the umbrella symptom of every reciprocity failure above. The report has been deprecated, which makes the failures even less visible than they used to be, but the underlying condition is unchanged: page A claims B as an alternate, B does not claim A back, and Google flags the asymmetry by ignoring it. The cure is always to complete the graph, and the diagnostic is always to check both directions of every edge.
Where to Put It — and Why You Must Not Mix
hreflang can be declared in three places. Each is valid. Mixing them within a single cluster is the error.
HTML <head>. <link rel="alternate" hreflang="en-gb" href="..."> in the head of each page. Simplest to reason about, but it bloats the head on a large cluster (a 30-locale site adds 30 link tags to every page) and works only for HTML documents. Easy to implement page-by-page, easy to get inconsistent across a template.
XML sitemap. Each <url> entry carries <xhtml:link rel="alternate" hreflang="..."> children listing all alternates. This is the most scalable method for large international sites because the entire cluster lives in one maintained file rather than scattered across every page’s rendered HTML, and it keeps hreflang out of the page weight entirely. The trade-off is that the sitemap must be meticulously maintained and the URLs in it must be the canonical, indexable, 200-status URLs.
HTTP Link header. Link: <https://example.com/de/>; rel="alternate"; hreflang="de-DE". This is the only option for non-HTML resources — PDFs, downloadable documents — because they have no <head> to hold a link tag. Inject these at the CDN or origin for the files that need them.
The non-negotiable rule: pick one method per cluster and use it consistently. Declaring some alternates in the head and others in the sitemap produces clusters that are partial in each location, and partial means non-reciprocal, and non-reciprocal means ignored. The mixing usually happens by accident — a migration moves most hreflang to the sitemap but leaves legacy head tags on some templates — and the result is a cluster that validates fine in any single location while being broken across them.
For most multi-region sites the sitemap method is the right default, and the reasoning is operational rather than theoretical. A site with 20 locales and 50,000 URLs that uses head-tag hreflang must emit 20 link tags into the head of every one of those pages — a million link tags in total, maintained across however many templates render them, every one a chance to drop a self-reference or a sibling. The same cluster in a sitemap lives in one generated file where the reciprocity is structurally guaranteed by the generator: emit the full set of alternates once per URL group and every page in the group is consistent by construction. The sitemap also keeps hreflang out of the rendered page weight entirely, which matters on the templates where the head is already crowded. The cost is that the sitemap must be regenerated whenever a URL changes or a locale is added — but that is a single build step, not a template change replicated across thousands of pages.
One more placement subtlety: hreflang in the sitemap requires the sitemap itself to be discoverable and the URLs it lists to be the canonical, indexable versions. An hreflang entry that points at a URL excluded from the index, redirected, or canonicalized elsewhere is a broken edge in the graph just as surely as a missing return tag. The alternate URLs must be the real, final destinations.
hreflang’s silent-failure nature is exactly what makes platform-level validation valuable. A technical-SEO platform reconstructs each cluster as a graph and checks the properties that page-level tools miss: full reciprocity across every edge, presence of a self-reference on every node, validity of every language-region code against the ISO standards, that every href resolves to a canonical 200-status URL rather than a redirect, and that the cluster is declared in exactly one location. It can flag the cluster that points to a non-200 target, the en-uk that should be en-gb, the x-default that points at a region-specific page, and the new locale that was added without updating its siblings — surfacing the specific broken edge and the corrected annotation rather than a generic “hreflang error” that leaves you to find the asymmetry by hand.