OneWebDesk

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 Link response 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.

IntentCorrectCommon mistakeWhy it fails
Korean, any regionkokrkr 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 regionenonly en-USen-US targets US users specifically; British, Australian and other English-speaking users fall outside the match.
British Englishen-GBen-UKISO 3166 assigns the United Kingdom the code GB. UK is not a standard code and the value is discarded.
Japanesejajpjp is the country; the language code is ja.
Country alonenot possibleKR, USThere 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?
Not directly. hreflang is a matching signal that decides which language version to show which user, not a ranking boost. Indirectly it helps: serving the right language improves click-through and engagement, and it prevents language versions from being treated as duplicates that split link equity.
Do I need x-default with only two languages?
It is optional but recommended. x-default names the version to serve users who match none of your declared languages — for a ko/en site, that is everyone searching in Spanish, German, and so on. Point it at your global default version or a language selector; reusing an existing language URL is fine.
Is it safer to put hreflang in both the head tags and the sitemap?
No — pick one method only. If the two sources ever disagree, you get conflicting signals and debugging becomes very difficult. Choose head tags, HTTP headers, or the sitemap, and keep it consistent site-wide.
Should I use en or en-US?
Use plain en unless your pages genuinely differ by country (currency, shipping, legal terms). en-US restricts the match to US users, leaving British, Australian and other English speakers outside the target. The country half is for market differences, not decoration.
Should untranslated pages be included in the hreflang cluster?
No. Only link pages whose language version actually exists. Declaring an English alternate that redirects to the homepage, or a URL that returns 404, undermines trust in the entire cluster and is a common reason Google discards the annotations.
How do I verify my hreflang setup?
Inspect the deployed pages directly. Use an SEO meta tag checker to extract each language URL's hreflang and canonical tags, confirm every page carries the identical alternate set, and confirm each canonical is self-referencing. If you use the sitemap method, validate the XML with a sitemap validator, and watch Search Console's hreflang error reports over time.

Tools to use with this guide

Related guides