Hreflang: How to Set Up Language and Country Versions Correctly
hreflang tells Google which language or country version of a page to show each searcher. This guide covers the three ways to declare it, the code rules Google actually enforces, return links, x-default, how canonicals interact with it, and step-by-step fixes for the errors an hreflang checker finds.
Searvia · 10/24/2026 · 11 min read
In short: hreflang tells Google which language or country version of a page to show each searcher. Every version lists every version, including itself, with an ISO 639-1 language code and an optional ISO 3166-1 alpha-2 region (
en,de,en-GB,pt-BR), plus an optionalx-defaultfallback. Links must be absolute and must point both ways, or Google ignores them. Each version needs a self-referencing canonical; a canonical that points to another language cancels the setup. Google detects the page language from the content, so the translation itself has to be real.
International sites lose traffic in a quiet way. The English page ranks in Germany, the Spanish page shows up in Mexico when you built a Mexican one, or the UK store appears for American searchers with prices in pounds. Nothing is broken on the site; Google simply guessed which version to show. hreflang is how you stop it guessing.
This guide explains how hreflang works according to Google's documentation, the code rules that trip most sites, how canonicals interact with it, what our free hreflang checker tests, and how to fix each problem.
What hreflang does, and what it doesn't
hreflang annotations describe a set of pages that are versions of each other: the same content in another language, or the same language adapted for another country. Google uses them to show the matching version in its results.
What it does not do:
- It does not detect language. Google says it "doesn't use hreflang or the HTML lang attribute to detect the language of a page"; it uses the visible content (multi-regional sites). A page tagged
dewith English text is an English page. - It does not make thin translations into duplicates or originals. Google says localized versions "are only considered duplicates if the main content of the page remains untranslated." A page with a translated menu and English body text is a duplicate.
- It does not redirect anyone. It only changes which URL appears in results. Visitors can still open any version.
The three ways to add hreflang
Google says the three methods "are equivalent from Google's perspective." Use one per page; mixing them just adds places for errors.
1. HTML link elements in the <head>, the most common choice. On https://example.com/en/pricing:
<link rel="alternate" hreflang="en" href="https://example.com/en/pricing" />
<link rel="alternate" hreflang="de" href="https://example.com/de/preise" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing" />
The German and French pages carry exactly the same four lines. Google requires the tags to be "inside a well-formed <head> section"; a stray element that the browser moves into the body, such as an image or an unclosed tag above the links, can break the head early.
2. HTTP Link headers, the only option for non-HTML files such as PDFs:
Link: <https://example.com/en/guide.pdf>; rel="alternate"; hreflang="en",
<https://example.com/de/guide.pdf>; rel="alternate"; hreflang="de"
Google says the header must be identical for every version.
3. XML sitemap, convenient for large sites because no template changes are needed:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/en/pricing</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
</url>
<url>
<loc>https://example.com/de/preise</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/pricing"/>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/preise"/>
</url>
</urlset>
Each <url> lists every version, including itself.
The rules Google enforces
Return links and self-reference
"If two pages don't both point to each other, the tags will be ignored." This is the rule behind most broken setups. If the English page lists the German page but the German page does not list the English one, Google drops the pair. Google also says "each language version must list itself."
Language and region codes
"Only language codes listed in ISO 639-1 and region codes listed in ISO 3166-1 Alpha 2 are supported." The language comes first; the region is optional. Google also supports script subtags for languages written in more than one script, such as zh-Hant and zh-Hans, and combinations like zh-Hans-US.
| Code | Valid? | Why |
|---|---|---|
en | Yes | English, any region |
en-GB | Yes | English for the United Kingdom |
pt-BR | Yes | Portuguese for Brazil |
zh-Hant | Yes | Chinese, Traditional script |
es-419 | No for Google | Region must be an ISO 3166-1 alpha-2 code; 419 (Latin America) is not one |
en-UK | No effect | The ISO code is GB; Google says UK, EU and UN have no effect |
be | Means Belarusian | A country code alone is invalid; be is the Belarusian language, not Belgium |
en_US | No | Use a hyphen, not an underscore |
eng | No for Google | Three-letter ISO 639-2 codes are not supported; use en |
For Spanish across Latin America, either use es for one shared Spanish version, or list the specific countries you target (es-MX, es-AR, es-CO) pointing to the same URL.
x-default
"The reserved x-default value is used when no other language/region matches the user's browser setting." Point it at a language selector or your main international version. It is optional but useful: without it, a searcher in a language you do not offer gets whichever version Google picks.
Absolute URLs
"Alternate URLs must be fully-qualified, including the transport method." https://example.com/de/ works; /de/ and //example.com/de/ do not.
hreflang and canonical tags
The two must agree. Each version should have a canonical pointing to itself, not to the English or main version. Google's canonicalization guide says to "specify a canonical page in the same language," or "the best possible substitute language if a canonical page doesn't exist for the same language," and notes that hreflang clusters influence canonical choice, favoring URLs that link to each other reciprocally.
The classic mistake: every language version carries <link rel="canonical" href="https://example.com/en/...">. That tells Google the other versions are duplicates of the English page, and they drop out of the index along with their hreflang annotations.
Two more conflicts to avoid: hreflang URLs that redirect (list the final URL) and hreflang URLs that carry noindex (a version Google cannot index cannot be shown).
Site structure and automatic redirects
Google's multi-regional guide compares the URL structures:
| Structure | Example | Notes from Google |
|---|---|---|
| Country domain | example.de | Clear geotargeting; expensive, one country per domain |
| Subdomain | de.example.com | Easy to set up; users may not recognize the targeting from the URL |
| Subdirectory | example.com/de/ | Low maintenance, same host |
| URL parameter | example.com?loc=de | Not recommended |
Whichever you choose, do not redirect visitors automatically by IP or browser language. Google says this "could prevent users (and search engines) from viewing all the versions of your site," and that Googlebot "usually originates from the USA." Show a suggestion banner instead and let hreflang handle search results.
How the Searvia hreflang checker works
The hreflang checker runs the same engine as our website SEO checker: you enter a domain or a page address, it audits that page (your home page if you enter only a domain) and the site files around it with 38 checks in about 20 seconds, and puts the three checks this tool is about first. This is exactly what they test.
Language versions, hreflang (low priority). The checker reads every <link rel="alternate" hreflang="..." href="..."> element in the HTML of that page, after following redirects to its final address.
- If there are none, the result is informational: "No language versions are declared (fine for a single-language site)." It does not affect your grade.
- Each code is checked against the codes Google supports:
x-default, or a two-letter ISO 639-1 language, optionally followed by a four-letter script (zh-Hant) and a two-letter ISO 3166-1 alpha-2 country (pt-BR,en-GB). A common mistake such asen-UKis marked with its fix (→ en-GB);es-419, three-letter languages and other codes Google does not accept are marked as not valid. Each row in the report shows the code, its URL, a check or a cross and that note. Up to 20 rows are shown. - It checks for a self-reference: one of the listed URLs must be the page itself. The comparison ignores
httpversushttps, a leadingwww., query strings and a trailing slash. - Pass when every code is valid and the page lists itself; if there is no
x-default, the message adds that this optional fallback is missing. Otherwise a warning with "The language versions do not include this page itself" or "Some language codes are not valid."
Page language (low priority). Passes when the <html> element has a lang attribute and shows its language; warns when it is missing.
Canonical URL (medium priority). Passes when the canonical tag points to the page itself; warns when it is missing or points to another address, showing that address.
What it does not test
- Return links. The checker reads one page and does not fetch the other versions, so it cannot confirm that they link back. Open each version's source, or use a site crawler, to check reciprocity.
- HTTP headers and sitemaps. Only HTML
<link>elements are read. If you declare hreflang in a sitemap, the checker reports no versions. - Absolute URLs and duplicates. Relative URLs are resolved instead of flagged, and if the same code appears twice, only one is kept.
- Other pages. One page is checked per run. Enter the address of each version you want to check (
example.com/de/pricing,de.example.com), or use a crawler for the whole site.
Reading the result
| Message | Meaning | Fix |
|---|---|---|
| No language versions are declared (fine for a single-language site). | No hreflang links on the page | Nothing for single-language sites; see step 1 otherwise |
| N language versions are declared correctly. | Codes valid and self-reference present; the message notes it if there is no x-default fallback (optional) | Still check return links by hand |
| The language versions do not include this page itself. | The page lists others but not itself | Step 2 |
| Some language codes are not valid. | At least one row has a red cross | Step 3 |
| The page does not declare its language. | No lang on <html> | Step 4 |
| The canonical URL points to another address. | The canonical names a different page | Step 5 |
The overall A+ to F grade covers all 38 checks, weighted by priority (high 10, medium 5, low 2; warnings earn half), with a "Fix these first" list and a shareable report.
How to fix every hreflang problem
1. No language versions are declared, but you have them
- List every version of the page and its full URL, for example
/en/,/de/,/fr/. - Pick a method: HTML tags for most sites, the sitemap if templates are hard to change.
- Generate the tags from one source in your code or CMS, so every version outputs the same complete set. Hand-written tags drift.
- If you use a multilingual plugin or your platform's built-in markets feature, it usually outputs hreflang for you; check that every translation is linked to its original and published, because unlinked translations get no tags.
- Rerun the hreflang checker.
2. The language versions do not include this page itself
Add a line for the current page with its own code. In a template, this usually means the loop over languages skips the current language; remove that condition. Make sure the self URL is the canonical URL, with the same protocol, host and trailing slash convention.
3. Some language codes are not valid
- Find the rows with a cross in the report.
- Fix the format: hyphen not underscore, language first, region second (
en-GB, notGB-en). - Replace unsupported codes:
en-UKbecomesen-GB(the report suggests it),es-419becomesesor a list of country codes, three-letter codes become two-letter ones. - Check the code is the language you mean:
beis Belarusian,czis not a language code (Czech iscs),jpis not a language code (Japanese isja),dkis not one (Danish isda).
4. The page does not declare its language
Add the language to the root element, for example <html lang="en"> or <html lang="pt-BR">. Google does not use it to detect language, but browsers, translation tools and screen readers do, as MDN describes.
5. The canonical points to another address
On a multilingual site this is often the cross-language canonical problem. Change each version's canonical to its own URL. If the canonical points to the same page in another form (http instead of https, or without www), make it match your final address exactly.
6. Return links are missing (not tested by the checker)
Open two versions side by side and compare their hreflang lists: they must be identical sets. If they differ, the cause is almost always the template or plugin that generates them, so fixing the generator once fixes every page.
Worked example (hypothetical)
The following is a hypothetical example, not real data. Kestrel Gear, an invented outdoor brand, runs example.com in English with /de/, /fr/ and /es/ versions. German searchers keep landing on English pages. The hreflang checker reports:
| Row | URL | OK |
|---|---|---|
en-UK | https://example.com/ | No (→ en-GB) |
de-DE | https://example.com/de/ | Yes |
fr | https://example.com/fr/ | Yes |
es_ES | https://example.com/es/ | No |
Canonical: "The canonical URL points to the page itself." Page language: en.
The cross on es_ES is a format error. The cross on en-UK comes with the note "→ en-GB": Google does not accept UK as a region, so the English page had no usable annotation. Viewing the German page's source finds a third: its canonical points to the English home page, so Google treated /de/ as a duplicate. The fixes:
- Changed the codes to
en,de,fr,es(one version per language, no regions needed) and addedx-defaultpointing to the English home page. - Moved the hreflang output into one shared template so every version prints the same set, including itself.
- Gave each version a self-referencing canonical.
- Removed an IP-based redirect that sent visitors from Germany to
/de/automatically, replacing it with a suggestion banner.
A 20-minute hreflang checklist
- Run the free hreflang checker on your home page and on each language version.
- Compare every code against ISO 639-1 and ISO 3166-1 alpha-2.
- Confirm each version lists itself and every other version.
- Open two versions and confirm the sets are identical.
- Check that each version's canonical points to itself.
- Add
x-default(optional, but useful). - Make sure no automatic redirect hides versions from Googlebot.
How Searvia helps
The free hreflang checker validates the hreflang links, page language and canonical of the page you enter in one shareable report, alongside the other checks of the website SEO checker. In a Searvia account, connect Search Console to see clicks and positions by country for each language version, and run the site audit every week to catch canonicals pointing elsewhere, duplicate titles across language versions and broken pages. If you publish apps as well as websites, our App Store localization guide covers the same problem on the store side. For the full technical checklist, see the technical SEO audit guide.
Frequently Asked Questions
- What is hreflang?
- hreflang is an annotation that tells Google about the language and country versions of a page, so it can show the right version to each searcher. It is written as rel="alternate" links with an hreflang attribute, in the HTML head, in an HTTP Link header or in an XML sitemap. Each version lists every version, including itself, with a code such as en, de or en-GB, and optionally an x-default fallback.
- Does hreflang help rankings?
- hreflang does not make a page rank higher. It helps Google pick which version of a page to show in a given language or region, so a German searcher sees the German page instead of the English one. Without it, Google may still work out the right version, but mistakes such as the wrong language in results are more likely when versions are similar.
- What is x-default in hreflang?
- x-default is a reserved hreflang value for the page to show when no other language or region in the set matches the user's browser settings. It is often a language selector page or the main international version. Google recommends it for the fallback page but does not require it.
- Is en-UK a valid hreflang code?
- No. hreflang region codes must be ISO 3166-1 alpha-2 country codes, and the code for the United Kingdom is GB, so use en-GB. Google says that using UK, EU or UN in hreflang annotations has no effect in Google Search. A country code on its own is also invalid: hreflang must start with an ISO 639-1 language code.
- Do I need hreflang if I use the lang attribute?
- Yes, if you have several language or country versions. Google says it does not use the HTML lang attribute or hreflang to detect the language of a page; it reads the visible content. The lang attribute helps browsers and screen readers, while hreflang tells Google how your versions relate to each other. Set both.
Sources
- Tell Google about localized versions of your page – Google Search Central
- Managing multi-regional and multilingual sites – Google Search Central
- How to specify a canonical URL – Google Search Central
- Redirects and Google Search – Google Search Central
- HTML lang global attribute – MDN Web Docs
- ISO 639 language codes – Library of Congress