The first international site I shipped had eleven language versions and zero hreflang. Traffic looked fine in aggregate, so nobody questioned it for months. Then a colleague in Berlin searched for our product on google.de and got the English page. Not the German one. The German one existed, was indexed, and was better written than the English original. Google just had no reason to prefer it, so it served whichever version had the most links pointing at it, which was the one we had been promoting on Twitter in English for two years.
That is the failure mode hreflang exists to prevent, and it is invisible from inside your own market. You search from your own country in your own language, get the right page, and conclude everything works. The people getting the wrong page are the ones you never hear from, because they bounced.
This guide covers hreflang end to end: what the annotation is, how the codes are built, the three places you can put it, the rules that make Google trust it, and how it interacts with canonical tags and sitemaps. It is the hub for a set of focused pieces on x-default, the errors that break implementations, hreflang versus canonical, and framework-specific setups for Next.js and WordPress.
TL;DR: An hreflang tag is a
rel="alternate" hreflang="xx-XX"annotation that tells search engines which language and regional version of a page to serve each user. The value is an ISO 639-1 language code, optionally plus an ISO 3166-1 alpha-2 region code, so the UK isGBand notUK. Annotations must be reciprocal: every page in a set lists every version including itself, or Google discards the set. You can implement it as HTML<link>tags, XML sitemap entries, or an HTTPLinkheader, but only one method per URL set. The hreflang generator builds all three formats in the browser and validates the codes before you ship them.
What is an hreflang tag?
An hreflang tag is an annotation that declares "another version of this page exists for this language and region, and it lives at this URL." It looks like this:
<link rel="alternate" hreflang="fr-CA" href="https://example.com/fr-ca/" />
Read left to right: rel="alternate" says this is a variant rather than a different page, hreflang="fr-CA" says the variant is for French speakers in Canada, and href gives the absolute URL. Nothing here is a directive. Google treats hreflang as a strong hint about which URL to swap in for a given searcher, not as a command it must obey, and it will override the hint when the evidence points elsewhere.
The annotation is defined in Google's documentation on localized versions of a page, and the code values come from BCP 47, the IETF standard for language tags. Bing supports the same annotation. Yandex does not use hreflang and relies on its own regional settings, which matters if Russian-language markets are part of your plan.
When do you need hreflang, and when is it a waste of time?
You need it when the same content exists in more than one language, or in one language aimed at more than one region. You do not need it for a site that exists only in English for only one market, and adding it there does nothing but add markup.
Three shapes come up in practice:
| Site shape | Example | Needs hreflang? |
|---|---|---|
| One language, one market | A US-only SaaS marketing site | No |
| Multiple languages | /en/, /de/, /ja/ versions of the same pages |
Yes |
| One language, multiple markets | example.com/us/ and example.com/uk/, both English |
Yes, and this is the case most often missed |
The third row is the one worth pausing on. Two English pages with different prices, shipping copy, and spelling look like duplicates to a crawler. Without hreflang, Google picks one and filters the other, which is how a UK store ends up invisible on google.co.uk while its US twin ranks. The annotation is what tells Google these are deliberate regional variants rather than accidental duplication.
How are hreflang codes built?
The value has two parts, and only the first is required.
The language is an ISO 639-1 two-letter code, lowercase: en, fr, de, ja, pt. The region is an ISO 3166-1 alpha-2 two-letter code, uppercase, joined with a hyphen: en-US, pt-BR, de-AT. The casing is convention rather than a hard requirement, because language tags are matched case-insensitively, but every code review goes faster when you follow it.
The rules people get wrong, in the order I see them get wrong:
- The United Kingdom is
GB.UKis not an ISO 3166-1 code.en-UKis silently ignored, which is worse than an error because nothing tells you. - Region alone is not a thing. There is no way to say "everyone in Canada" without naming a language.
hreflang="CA"is invalid. You writeen-CAandfr-CAas separate entries. - Latin America is
es-419. That is a UN M49 numeric region code, valid in BCP 47, and the right answer when your Spanish is aimed at the region rather than at Spain or Mexico specifically. - Chinese usually wants a script, not a country.
zh-Hantfor Traditional andzh-Hansfor Simplified describe the writing system, which is what readers care about.zh-CNandzh-TWwork but conflate script with geography. - Language and region are independent axes.
en-CAandfr-CAcan both exist. So canen-US,en-GB, anden-AU. Do not assume one page per country or one page per language; map the combinations your site genuinely serves.
The hreflang generator checks each code against the expected shape and flags subtags that are not recognized language or region values, so en-UK fails at the keyboard instead of in a crawl three weeks later.
Where do hreflang tags go?
Three implementation methods are supported, and they are equivalent in effect. The choice is operational.
| Method | Where it lives | Best for | Cost |
|---|---|---|---|
HTML <link> |
Each page's <head> |
Most sites; easiest to debug | Every page carries the full set, which grows with the language count |
XML sitemap xhtml:link |
Sitemap entries | Large sites, many locales | Sitemap must stay in sync; slower to be picked up |
HTTP Link header |
Server response | PDFs and other non-HTML files | Needs server config; invisible in the browser's view-source |
HTML tags in the head:
<link rel="alternate" hreflang="en-US" href="https://example.com/us/pricing" />
<link rel="alternate" hreflang="en-GB" href="https://example.com/uk/pricing" />
<link rel="alternate" hreflang="de-DE" href="https://example.com/de/preise" />
<link rel="alternate" hreflang="x-default" href="https://example.com/pricing" />
The same set as sitemap entries, which needs the xhtml namespace declared on <urlset>:
<url>
<loc>https://example.com/us/pricing</loc>
<xhtml:link rel="alternate" hreflang="en-US" href="https://example.com/us/pricing"/>
<xhtml:link rel="alternate" hreflang="en-GB" href="https://example.com/uk/pricing"/>
<xhtml:link rel="alternate" hreflang="de-DE" href="https://example.com/de/preise"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/pricing"/>
</url>
And as an HTTP header, which is the only option for a PDF:
Link: <https://example.com/us/whitepaper.pdf>; rel="alternate"; hreflang="en-US",
<https://example.com/de/whitepaper.pdf>; rel="alternate"; hreflang="de-DE"
Pick one method per URL set. Mixing HTML tags and sitemap entries for the same pages is not fatal, but it gives Google two sources that can drift apart, and the day they disagree you will not know which one won. If you are already producing a sitemap with the XML Sitemap Generator, the sitemap format drops straight into it.
Why must hreflang be reciprocal?
Because a one-way claim is unverifiable. If your English page says "the French version is over there" and the French page says nothing, anyone could point at anyone. Google's rule is that both sides must agree, and the return-tag requirement is stated plainly in its documentation: annotations without confirming return tags are ignored.
The practical form of the rule is simpler than it sounds. Put the complete, identical set of annotations on every page in the group, including a self-referential entry for the page itself. The English page lists English, French, German, and x-default. So does the French page. So does the German page. Same block, byte for byte, on all of them.
That redundancy is the feature. It makes the set self-verifying and it makes deployment mechanical: build the block once per URL group and render it from a single source rather than hand-editing per page. Hand-editing is how a set of nine pages ends up with eight correct blocks and one missing entry, and one missing entry is enough for Google to distrust the pair it belongs to.
What does x-default do?
x-default names the page to serve when no other entry matches the user. Someone browsing in Korean lands on a site with English, French, and German versions: without x-default, Google guesses. With it, you decide, and the answer is usually a language selector or your primary international homepage.
It is not a language code and it does not replace one. Your English page still needs its own en or en-US entry, and x-default sits alongside as a catch-all. Google treats it as optional, and I treat it as required, because "the algorithm picks something" is not a routing strategy. The full x-default guide covers where to point it, what happens when you point it at a redirect, and why a language selector page needs care of its own.
How do hreflang and canonical tags interact?
They answer different questions and they must not contradict each other.
A canonical tag says "of these near-identical URLs, index this one." An hreflang annotation says "these URLs are localized alternates, serve the right one per user." The failure that eats whole language versions is canonicalizing across languages: your French page declares the English page as canonical, which tells Google to drop the French URL from the index, and hreflang cannot resurrect a URL that has been canonicalized away.
The rule is short. Every page canonicalizes to itself. Hreflang describes the relationships between those self-canonical pages. Get those two facts straight and the two systems cooperate; get them crossed and the stronger signal, canonical, wins and quietly deletes your translations. Hreflang vs canonical works through the combinations, including the parameter-URL case where both are in play at once.
How do you test that hreflang is working?
Start with Search Console. The International Targeting report lists the two failures that matter at scale, "no return tags" and "unknown language code", across the whole property, which beats inspecting pages one at a time. Note that it lags: fixes show up only after Google recrawls every page in the affected set, so a week of no change is normal and not evidence that the fix failed.
Then crawl your own site. Any crawler that surfaces hreflang annotations will show you the set each URL carries, and the check you are running is whether every page in a group carries the identical full block. Sort by group and eyeball the counts; a page with four entries in a group of five is the bug.
Then check the targets resolve. Each href must be an absolute URL that returns 200 and is indexable. A target that redirects still works but adds a hop, a target that 404s poisons the set, and a target that is noindex is a contradiction: you are advertising a page you have told Google to ignore.
What breaks most often?
In rough order of how often I find them:
- Missing return tags. Page A points at B, B does not point back. Google discards the pair.
- No x-default. Unmatched users get whichever version Google guesses.
- Non-ISO codes.
en-UKleads, followed by invented ones likeen-EU. - Relative URLs. hreflang requires absolute URLs, protocol and host included.
- Trailing-slash mismatches.
/fr/and/frare different URLs, and the annotation must match the indexable form. - Canonical pointing across languages. Cancels out the whole set.
- Mixed methods. HTML tags and sitemap entries for the same URLs, drifting apart.
Every one of these produces markup that looks right. That is what makes hreflang tedious: the failure is always silent. 12 common hreflang errors walks through each with the symptom you would observe and the fix.
How do you generate and maintain the tags?
For a handful of URLs, hand-writing the block is fine once and painful on the second change. For anything larger, generate it.
The hreflang tag generator takes one row per version - a code and an absolute URL - and emits the block as HTML link tags, sitemap entries, or an HTTP header. It validates each code against the ISO shape, flags unrecognized subtags, catches duplicate codes, rejects relative URLs, and warns when the set has no x-default. It runs entirely in the browser, so your URL structure, which maps your whole international architecture, is never uploaded anywhere.
For a site that is already templated, the better long-term answer is to render the block from your routing configuration so it cannot drift. That is what the Next.js and WordPress guides cover: one source of truth for the locale map, one component or filter that renders the full reciprocal set on every page in the group. The generator is then the thing you use to check the output rather than to produce it, which is the right division of labour once a site is past a few dozen URLs.
If you are assembling the rest of the technical SEO layer at the same time, the meta tag generator handles the per-page head and the robots.txt generator covers crawl directives. The hreflang tool guide goes deeper on the generator itself.
Frequently asked questions
What is an hreflang tag?
An hreflang tag is a rel="alternate" hreflang="xx-XX" annotation that tells search engines which language and regional version of a page to serve a given user. It helps international sites rank the right version in each market and stops near-identical regional pages from being filtered as duplicates.
Do I need hreflang if my site is only in English?
Only if you target more than one English-speaking market with separate URLs. A single English site for a single market does not need hreflang. Separate US and UK stores, both in English, do need it, because without the annotation the two look like duplicates.
Is the region part of an hreflang code required?
No. The language code is required and the region is optional. en is valid on its own. What is not valid is a region without a language: there is no way to target a country without naming a language.
Does hreflang guarantee which page Google shows?
No. Hreflang is a strong signal, not a directive. Google can still serve a different version when other evidence, such as the user's explicit language settings or the query language, points elsewhere.
How long does it take for hreflang changes to take effect?
Google has to recrawl every page in the set before it can confirm the return tags, so changes typically take days to weeks depending on how often your site is crawled. The International Targeting report in Search Console updates on the same lag.
Can I use hreflang and canonical tags together?
Yes, and you should. Each page canonicalizes to itself, and hreflang describes the relationships between those self-canonical pages. Canonicalizing one language version to another removes it from the index and cancels the hreflang set.
Does Bing support hreflang?
Yes. Bing supports the same rel="alternate" hreflang annotation. Yandex does not use hreflang and relies on its own regional settings instead.
Where should hreflang tags go, in the HTML or the sitemap?
Either works. HTML link tags are easier to debug and are the common choice. Sitemap entries keep page heads lean and suit sites with many locales. Use one method per URL set rather than both.



