Setting Up hreflang: Stop Multilingual SEO Duplication
Configure hreflang correctly for multilingual sites and avoid the x-default and return-tag mistakes.
Run a site in two languages and sooner or later you hit one of two problems. Either the wrong-language page ranks — a Korean user searches, Google serves the English page, and they bounce — or Google treats /ko/pricing and /en/pricing as near-duplicates, splitting link equity between them or quietly dropping one from the index. hreflangis the annotation that solves both at once: it tells search engines “these URLs are language variants of the same content — serve each user the one that matches their language.”
The catch is that hreflang is probably the most frequently botched tag in technical SEO. Miss one side of a reciprocal reference, write kr instead of ko, or point a canonical at the wrong language, and Google silently discards the annotation — no error, no warning, just no effect. This guide walks through choosing one of the three implementation methods, the code rules, x-default, the return-tag requirement, and the canonical trap, ending with a worked ko/en example shown correct and then deliberately broken. You can generate the full block with the hreflang tag generator and verify what actually shipped with the SEO meta tag checker.
Three ways to implement it — pick exactly one
There are three delivery channels for hreflang. All three carry equal weight, but you should commit to a single one. Mixing methods means that when the two sources disagree — and eventually they will — debugging becomes guesswork.
- 1.
<link>tags in the HTML<head>— the most common choice. Each page lists<link rel="alternate" hreflang="en" href="..." />for every variant. Easy to manage per page in a CMS or a framework like Next.js. The downside: with ten languages, every page carries ten-plus lines of markup. - 2. HTTP
Linkresponse headers — for non-HTML resources like PDFs, where there is no head to put tags in:Link: <https://example.com/en/file.pdf>; rel="alternate"; hreflang="en". Rarely worth it for regular pages. - 3. The XML sitemap — each
<url>entry carries<xhtml:link rel="alternate" hreflang="...">children. Everything lives in one file with no page markup changes, which scales best for large sites with many languages. The sitemap gets big, and hand-maintaining reciprocity is error-prone, so generate it programmatically — then run it through the sitemap validator to confirm the XML parses and the URLs resolve.
Whichever channel you choose, every href must be an absolute URL — protocol and host included. Relative paths like href="/en/pricing" are invalid and get ignored.
Code rules: language-COUNTRY, and the kr / en-UK traps
An hreflang value is an ISO 639-1 language code (two lowercase letters) on its own, or a language-COUNTRY pair using ISO 3166-1 Alpha-2. This is where most mistakes happen, because country codes and language codes are separate systems that only sometimes look alike.
| Intent | Correct | Common mistake | Why it fails |
|---|---|---|---|
| Korean, any region | ko | kr | kr is South Korea’s country code, not a language. The language is ko; ko-KR is valid but narrows the audience to Korea only. |
| English, any region | en | only en-US | en-US targets US users specifically; British, Australian and other English-speaking users fall outside the match. |
| British English | en-GB | en-UK | ISO 3166 assigns the United Kingdom the code GB. UK is not a standard code and the value is discarded. |
| Japanese | ja | jp | jp is the country; the language code is ja. |
| Country alone | not possible | KR, US | There is no country-only targeting in hreflang. The format is language, optionally followed by a country. |
The rule of thumb: language is mandatory, country is optional. Add a country only when pages genuinely differ by market — currency, shipping, legal text (en-GB vs en-US). Otherwise a bare ko or en matches the widest audience.
x-default and return tags — the conditions for being trusted
x-default names the page to serve users who match none of your declared languages — typically a language-selector page or your global default version (often English). It is optional but worth including: without it, a Spanish-speaking user searching your ko/en site leaves Google guessing which version to show. It is perfectly fine for x-default to point at the same URL as one of your language versions.
Then comes the single most important rule in all of hreflang — reciprocity:
- Every language version must list the complete set of alternates, including itself (the self-referencing tag).
- If page A points to page B, page B must point back to A — the return tag.
- One missing side invalidates the pair. Google reasons: “B does not acknowledge A, so A’s claim cannot be trusted” — and drops both annotations. This is deliberate: it stops third parties from declaring your pages as alternates of theirs.
In practice this means that with N languages, every page in the cluster carries the identical N+1-line block (alternates plus x-default). If any page’s block differs, reciprocity is broken somewhere. Extract the rendered tags for each URL with the SEO meta tag checker and diff them — mismatches jump out immediately.
The canonical trap: self-referencing per language, always
Perfect hreflang is worthless if canonicals contradict it. The classic mistake is pointing the English page’s canonical at the Korean original “to consolidate duplicates”:
<!-- on /en/pricing — WRONG --> <link rel="canonical" href="https://example.com/ko/pricing" /> <link rel="alternate" hreflang="en" href="https://example.com/en/pricing" />
This sends Google two contradictory instructions at once: “don’t index /en/pricing, /ko/pricing is the representative” and “serve /en/pricing to English users.” Canonical usually wins, the English page drops out of the index, and the hreflang cluster collapses. The rule is simple: each language version’s canonical points to itself. Language variants are not duplicates to consolidate — they are independent pages that each deserve indexing, and preventing the duplicate verdict is hreflang’s job, not canonical’s.
Worked example: a ko/en pair — correct block vs the block Google ignores
Say you have https://example.com/ko/pricing (Korean) and https://example.com/en/pricing (English), with English as the global default. Both pages must carry exactly this block — only the canonical differs per page:
<!-- <head> of /ko/pricing --> <link rel="canonical" href="https://example.com/ko/pricing" /> <link rel="alternate" hreflang="ko" href="https://example.com/ko/pricing" /> <link rel="alternate" hreflang="en" href="https://example.com/en/pricing" /> <link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing" /> <!-- <head> of /en/pricing --> <link rel="canonical" href="https://example.com/en/pricing" /> <link rel="alternate" hreflang="ko" href="https://example.com/ko/pricing" /> <link rel="alternate" hreflang="en" href="https://example.com/en/pricing" /> <link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing" />
Notice the three alternate lines are byte-for-byte identical on both pages. Now the broken variant you see in the wild all the time:
<!-- /ko/pricing — points only at the English version (self missing) --> <link rel="alternate" hreflang="en" href="https://example.com/en/pricing" /> <!-- /en/pricing — no hreflang at all -->
This fails on two counts. First, the Korean page omits its own self-referencing ko tag, which is required. Second, the English page has no return tag: the ko→en claim exists but the en→ko confirmation does not, so Google cannot verify the pair and treats the whole setup as if no tags existed. There is no partial credit — hreflang is all-or-nothing per pair. Since hand-writing these blocks creates pages × languages opportunities to slip, feed your URL pattern and language list into the hreflang tag generator and emit the whole set at once. If you ship via sitemap instead, finish by running the sitemap validator and confirm every alternate URL returns 200 — listing a redirecting or 404 URL as an alternate is another classic cluster-killer.
Frequently asked questions
Does hreflang improve rankings?
Do I need x-default with only two languages?
Is it safer to put hreflang in both the head tags and the sitemap?
Should I use en or en-US?
Should untranslated pages be included in the hreflang cluster?
How do I verify my hreflang setup?
Tools to use with this guide
Related guides
- How to Write robots.txt: Syntax, Examples and Common Mistakesrobots.txt rule syntax, ready-made examples by site type, and the common mistakes that break indexing.
- 301 vs 302 vs 307/308: Choosing the Right RedirectThe difference between redirect status codes and how to pick the right one for SEO and method preservation.