Here is the problem. You run an online store with a UK English site, a US English site, and a Spanish site. The product pages are near-identical — same product, same images, prices and spelling adjusted per market. To a search engine these three pages look like duplicate content, and it has to guess which one to rank and serve. Left to guess, it serves your US page to a UK shopper (wrong currency, wrong spelling), or buries two of the three versions as duplicates of the first. Either way, a user lands on the wrong page.
Hreflang is the instruction that fixes this. It tells search engines: "these pages are alternate language or regional versions of each other — here is the full set, and here is the language and region each one targets." The search engine then serves the right version to the right user based on their language and location, and treats the cluster as a coordinated group rather than competing duplicates. This guide covers the syntax, the codes, the return-tag requirement that trips up nearly everyone, how hreflang interacts with canonical tags, and how to verify the whole thing is working.
What Hreflang Looks Like
An hreflang annotation is a set of <link rel="alternate"> tags in the <head>, one per language/region version:
<link rel="alternate" hreflang="en-gb" href="https://example.com/uk/widget/">
<link rel="alternate" hreflang="en-us" href="https://example.com/us/widget/">
<link rel="alternate" hreflang="es" href="https://example.com/es/widget/">
<link rel="alternate" hreflang="x-default" href="https://example.com/widget/">
Each tag names a version and the URL that serves it. The href must be an absolute URL — protocol and domain included. Relative URLs are a silent killer here; search engines do not reliably resolve them in hreflang context, and the whole annotation can be ignored.
Three Ways to Implement It — Pick One
There are three valid methods of delivering hreflang. They are equivalent in effect; the right choice depends on your site.
- HTML head link tags. The example above. The simplest method for most sites, easiest to debug, easiest for tools to read. Downside: every page in a cluster must list every other page, so a 50-language site adds 50 tags to every page.
- HTTP headers. The same annotations sent as
Link:response headers. This is the only way to set hreflang on non-HTML resources like PDFs, where there is no<head>to put a tag in. - XML sitemap. hreflang declared via
<xhtml:link>elements inside your sitemap. Keeps the markup out of your page<head>entirely, which is the cleanest option for very large multilingual sites. Downside: harder to spot-check by hand.
The important rule: use one method per site. Do not mix head tags and sitemap entries for the same pages — at best it is redundant, at worst the two disagree and you have created a debugging nightmare. Choose the method that fits your stack and apply it consistently.
Language and Region Codes
The value of the hreflang attribute is a language code, optionally followed by a region code:
- Language — an ISO 639-1 code:
en,es,fr,de,pt,ja. Lowercase by convention. - Region (optional) — an ISO 3166-1 Alpha-2 country code, separated by a hyphen:
en-gb,en-us,es-mx,pt-br. Uppercase by convention, though the value is case-insensitive.
Language alone (es) targets all speakers of that language regardless of country. Language plus region (es-mx) targets Spanish speakers in Mexico specifically. Use language-only when one version serves all regions, and add a region only when you genuinely have a country-specific version. You cannot specify a region without a language — hreflang="us" on its own is invalid.
The code mistakes that bite
ukinstead ofgb. The country code for the United Kingdom isGB, notUK.en-ukis invalid and will be ignored — this is one of the most common hreflang errors in the wild. Useen-gb.en-euand other made-up regions. The region must be a real ISO 3166-1 country. The EU is not a country, soen-euis invalid. There is no code for "Europe" or "Latin America" — target individual countries, or use language-only.- Swapping the order. It is language-then-region:
en-gb, nevergb-en. - Using
en-uk,zh-cncasing confusion. Case does not actually matter to search engines, but stick to the lowercase-language, uppercase-region convention so your markup is readable and reviewable.
The x-default Value
The special value x-default marks the fallback page — the version to serve when no other hreflang entry matches the user's language and region:
<link rel="alternate" hreflang="x-default" href="https://example.com/widget/">
Use it for the page that handles "everyone else." Common patterns: a language-selector landing page, a generic English page that catches users whose language you do not specifically target, or a global homepage that redirects based on geolocation. If you have a UK, US, and Spanish version and a French speaker arrives, x-default decides what they get instead of leaving the search engine to guess. It is optional, but on any serious international setup you want one.
The Return Tag Requirement
This is the single most important rule in hreflang, and the source of the number-one error: hreflang must be bidirectional.
If page A links to page B with an hreflang tag, page B must link back to page A. Every page in a cluster has to reference every other page in the cluster — and itself. These back-references are called return tags. If a return tag is missing, search engines treat the annotation as unconfirmed and may ignore it entirely.
Concretely, with three versions, the same complete block appears on all three pages:
<!-- This identical block goes on the UK, US, AND ES pages -->
<link rel="alternate" hreflang="en-gb" href="https://example.com/uk/widget/">
<link rel="alternate" hreflang="en-us" href="https://example.com/us/widget/">
<link rel="alternate" hreflang="es" href="https://example.com/es/widget/">
<link rel="alternate" hreflang="x-default" href="https://example.com/widget/">
Notice that each page also lists itself — the UK page includes the en-gb tag pointing at its own URL. That is the self-referencing hreflang, and it is required, not optional. A page that lists its siblings but omits itself is an incomplete annotation.
The practical takeaway: build the hreflang block once, then output the exact same block on every page in the cluster. If you generate it dynamically and each page only ever knows about its siblings, you will drop return tags. Generate the full set, every time.
Hreflang and Canonical Tags
Hreflang and canonical tags must be consistent, and getting their relationship wrong silently breaks both.
The rule: each language version canonicals to itself, not across languages. The Spanish page's canonical points to the Spanish URL; the UK page's canonical points to the UK URL. They are different pages serving different audiences — they are not duplicates of each other, even though hreflang declares them as alternates.
<!-- On the Spanish page -->
<link rel="canonical" href="https://example.com/es/widget/"> <!-- self -->
<link rel="alternate" hreflang="en-gb" href="https://example.com/uk/widget/">
<link rel="alternate" hreflang="en-us" href="https://example.com/us/widget/">
<link rel="alternate" hreflang="es" href="https://example.com/es/widget/">
The classic mistake is pointing every language version's canonical at the English page — for example the Spanish page canonicalising to /uk/widget/. That tells the search engine "the Spanish page is a duplicate of the UK page, index the UK one instead," which de-indexes your Spanish content and makes the hreflang annotation self-contradictory. Hreflang says "these are distinct alternates"; the cross-language canonical says "no they're not." The two instructions conflict, and you lose. Canonical points to self within each language version, always.
Common Mistakes
1. Missing return tags
Page A references B but B does not reference A. The most common hreflang error by a wide margin. The annotation goes unconfirmed and is ignored. Always output the full bidirectional set.
2. Wrong or invalid codes
en-uk instead of en-gb, en-eu for "Europe," region-only values like us, or codes that are not real ISO 639-1 / 3166-1 entries. Invalid codes are silently dropped.
3. Canonical pointing across languages
Each version canonicalising to the English page instead of to itself. Contradicts hreflang and de-indexes the alternates. Self-canonical per language version.
4. Relative URLs
href="/es/widget/" instead of the full absolute URL. Hreflang requires absolute URLs with protocol and domain; relative ones can cause the whole annotation to be ignored.
5. Only tagging some pages
Hreflang on the homepage cluster but not on product or category pages. hreflang works per-URL — each set of alternate pages needs its own annotation. Partial coverage means partial protection.
6. Linking to non-200 URLs
An hreflang href pointing at a redirect or a 404. The alternate must resolve directly to a live 200 page, just like a canonical. Broken targets break the cluster.
7. Mismatched clusters
The UK page lists three alternates, the US page lists two, the ES page lists four. Every page in a cluster should declare the identical set. Inconsistent sets confuse the consolidation.
How to Verify Hreflang
- Run any URL through Meta Tag Checker — it surfaces the alternate tags alongside the canonical and the rest of your meta tags, so you can confirm the language codes and URLs at a glance.
- View Source in the browser and search for
hreflang— confirm every expected version is present, including the self-reference and anyx-default. - curl and grep across the cluster:
curl -s https://example.com/es/widget/ | grep hreflang. Run it on each URL and check the blocks match. - Google Search Console. The international targeting and indexing reports flag return-tag errors and unknown language codes — historically surfaced in the dedicated hreflang report, and still reported through Search Console's coverage and URL Inspection tooling. This is the authoritative check for whether Google has actually accepted your annotations.
- Third-party crawlers (Screaming Frog, Sitebulb, Ahrefs) crawl the full cluster and explicitly flag missing return tags, invalid codes, and broken alternate URLs across the whole site at once — the fastest way to audit a large multilingual setup.
The crawler and Search Console checks complement each other: the crawler tells you whether your markup is internally consistent, Search Console tells you whether the search engine accepted it.
The Production Habit
For new international pages:
- Generate the full hreflang block — every version plus a self-reference — and output the identical block on every page in the cluster.
- Use absolute URLs, always.
- Use valid ISO codes:
en-gbnoten-uk, real countries only, language-then-region order. - Add an
x-defaultfor the catch-all version. - Keep each version's canonical pointed at itself, never across languages.
- Pick one delivery method — head tags, headers, or sitemap — and stick to it site-wide.
For existing sites:
- Crawl the whole site to find missing return tags, invalid codes, and broken alternate URLs.
- Spot-check that every template type — homepage, category, product, article — actually emits hreflang.
- Confirm canonicals are self-referencing per language and not collapsing the cluster.
- Re-audit after any URL structure change, new market launch, or template edit.
Hreflang is unforgiving in the small details and invisible when it fails — pages just quietly rank in the wrong markets. Build the block correctly once, output it consistently, and verify it. Meta Tag Checker takes the guesswork out of confirming the alternates and canonical on any URL you want to check.