Fixing Invalid Hreflang Language Codes
Every hreflang value has to be a real language code, optionally followed by a real country code.
When a value is wrong, such as en-UK, en_GB or a country code like cz where a language belongs, Google does not warn you; it simply drops that version from the cluster, so the page you built for that audience competes with its siblings instead of being matched to them. This guide explains the exact format, walks through the mistakes that look correct at a glance, shows where wrong codes come from in content systems, and gives you a way to validate every value before it goes live.
The anatomy of a valid value
A hreflang value is built from up to two parts joined by a hyphen:
- Language, required: a two-letter code from ISO 639-1, the international list of
language codes.
enis English,deis German,plis Polish. - Region, optional: a two-letter code from ISO 3166-1 alpha-2, the international list
of country codes.
GBis the United Kingdom,ATis Austria,MXis Mexico.
So de means "German, for anyone", and de-AT means "German, for people in
Austria". The order never changes: language first, region second. A region can never appear on its own,
and the special value x-default is the only thing allowed that does not follow the pattern.
Chinese is the common case for a third ingredient. Because the same language is written in two scripts,
sites often need to separate Simplified from Traditional characters. Google recognises script subtags
such as zh-Hans and zh-Hant, and region-based values such as zh-CN,
zh-TW and zh-HK are also valid. Choose one approach for the whole cluster and
keep to it.
Letter case is not significant to Google. en-gb, EN-GB and en-GB
are read the same way. The convention of lower-case language and upper-case region is still worth following,
because it makes mistakes easier to spot when you read the source.
en_GB,
come from software locale settings and are not valid hreflang, even though they look official.Values that look right but are not
Most invalid codes are not typing slips. They are reasonable guesses based on country names, domain endings or software settings. The table groups the mistakes by where they come from, so you can recognise the pattern on your own site.
| Value found | Why it fails | Use instead |
|---|---|---|
en-UK | UK is a common abbreviation, but the ISO country code is GB | en-GB |
en_GB, fr_CA | Underscore format from WordPress, PHP or Java locale settings | en-GB, fr-CA |
cz | Taken from the .cz domain; it is not a language code at all | cs
or cs-CZ |
ua | The country code for Ukraine, not the language | uk or uk-UA |
kr | Real code, wrong language: it means Kanuri, not Korean | ko
or ko-KR |
ee for Estonia | Real code, wrong language: it means Ewe | et or et-EE |
br for Brazil | Real code, wrong language: it means Breton | pt-BR |
iw, in, ji | Old codes withdrawn in favour of new ones; some software still emits them | he, id, yi |
es-419 | A numeric area code, outside the format Google documents for hreflang | Plain
es, or one value per country |
en-INT, en-WW | Invented regions meaning "international" | Plain
en, or x-default |
fr-BE-nl, de-CH-FR | Two regions or a language tacked on the end | One language and at most one region |
english, German | Language names written out | en, de |
The "real code, wrong language" rows are the most dangerous. A validator that only checks whether
a value exists will pass kr, ee and br, but you have told Google
the page is in Kanuri, Ewe or Breton. The value is technically well formed and completely wrong for your
audience, so it is worth checking the meaning of every code, not just its shape.
Where wrong codes come from
Fixing a code in the page source only lasts until the next deployment. Find the setting that produced it.
- CMS language settings. Many systems store a locale for each language, such as
en_GB, and some templates print that locale straight into the hreflang attribute. The fix is to convert it, replacing the underscore with a hyphen, or to use the separate hreflang or language code field if the system has one. - URL folders reused as codes. A site with folders called
/uk/,/cz/or/jp/sometimes builds hreflang from the folder name. The folder can stay as it is, since URLs are not required to match codes, but the annotation must use the real code. - Country selectors. A store organised by country rather than language may have no language field at all, so developers fill the gap with the country code. Add an explicit language for each market.
- Translation service exports. Some services label languages with their own identifiers,
such as
pt-brorzh-cn, which are fine, orzh-chsandno-bok, which are not. - Hard-coded tags in themes. Old theme files may include hreflang lines written years ago for a market that has since changed.
A related check: the lang attribute and og:locale
The same codes appear in two other places, and it helps to keep them consistent. The lang
attribute on the html element uses the hyphen format, for example <html lang="en-GB">,
and helps browsers and screen readers. The Open Graph og:locale tag is different: it uses
the underscore format, for example en_GB. That difference is a common source of confusion,
because code copied from og:locale into hreflang produces an invalid value.
When to add a region, and when to leave it out
A region narrows a page to one country. Add it only when the content actually changes by country:
prices and currency, delivery terms, legal notices, spelling conventions or product ranges. If you have
one German page that serves Germany, Austria and Switzerland equally, plain de describes
it accurately, and inventing three region codes for the same page adds lines without adding meaning.
There is also a gap to avoid. If your cluster contains only en-GB and en-US,
an English speaker in Canada or Australia matches neither. Adding a language-only value, such as en
pointing at your most general English page, catches everyone else who reads that language. Google uses
the language-only value as the fallback for users of that language whose region is not listed separately.
<link rel="alternate" hreflang="en-GB" href="https://example.com/uk/">
<link rel="alternate" hreflang="en-US" href="https://example.com/us/">
<link rel="alternate" hreflang="en" href="https://example.com/uk/">
<link rel="alternate" hreflang="de" href="https://example.com/de/">
<link rel="alternate" hreflang="x-default" href="https://example.com/">
Here the British page is listed twice: once for the UK, and once as the general English page. That is allowed. Every page in the cluster must still carry exactly the same five lines.
Validating codes automatically
Shape checks catch underscores, invented regions and extra parts. A regular expression covering language, optional script and optional region looks like this:
^(x-default|[a-z]{2}(-[A-Za-z]{4})?(-[A-Za-z]{2})?)$
Shape is only half the job. The value also has to exist, and the language has to be the one you meant.
A short Python check that uses the langcodes package, or your own copies of the ISO lists,
can test both:
import re
LANGS = {"en", "de", "fr", "cs", "uk", "ko", "et", "pt", "zh", "he"} # load the full ISO 639-1 list
REGIONS = {"GB", "IE", "US", "DE", "AT", "CZ", "UA", "KR", "EE", "BR", "CN", "TW"} # full ISO 3166-1 alpha-2 list
EXPECTED = {"/cz/": "cs", "/ua/": "uk", "/kr/": "ko"} # folder to intended language
def check(value, url):
if value == "x-default":
return "ok"
m = re.fullmatch(r"([a-z]{2})(?:-([A-Za-z]{4}))?(?:-([A-Za-z]{2}))?", value, re.I)
if not m:
return "bad format"
lang, script, region = m.group(1).lower(), m.group(2), m.group(3)
if lang not in LANGS:
return "unknown language " + lang
if region and region.upper() not in REGIONS:
return "unknown region " + region
for folder, want in EXPECTED.items():
if folder in url and lang != want:
return "language " + lang + " but folder expects " + want
return "ok"
The expected-language map is what catches kr on a Korean section. Keep the ISO lists
up to date from the official sources rather than typing them from memory.
The fix, step by step
Step 1
Collect every hreflang value in use
Run the Hreflang Checker on the main page of each locale and let it follow the clusters, then add any values from your XML sitemap and HTTP headers if you use them. You want a list of every distinct value the site emits, together with one example page for each. On most sites the list is short, often under twenty values, which makes the rest of the work quick once the list exists and is complete.
Step 2
Split each value into language and region
Break each value at the hyphen into its parts. Anything with an underscore, a space,
more than one region, a written-out language name or an invented suffix fails at this stage and goes
straight onto the fix list. Values with a four-letter middle part, such as zh-Hant, are
script subtags and are allowed. Note which values have no region, because you will decide later whether
that is intentional.
Step 3
Check the language part against ISO 639-1
Look up each language part in the ISO 639-1 list and confirm two things: that the code
exists, and that it names the language the page is actually written in. This is where cz,
ua and jp fail because they are not language codes, and where kr,
ee and br fail because they belong to other languages. Replace withdrawn codes
such as iw with current ones such as he.
Step 4
Check the region part against ISO 3166-1 alpha-2
For values with a region, confirm the two letters are an assigned country code.
UK is the classic failure and must become GB. Continents, trading blocs and
invented labels such as EU, LA, INT or WW are not
usable regions, and numeric area codes like 419 fall outside the documented format. Replace them with
plain language values, separate values per country, or the x-default fallback.
Step 5
Decide whether you need a region at all
For each language, ask whether the content genuinely differs by country. If one Spanish
page serves every Spanish speaker, use plain es. If you have separate pages for Spain and
Mexico, use es-ES and es-MX, and consider adding plain es pointing
at the more general page so Spanish speakers in other countries still match. Write the final list of
values down as your locale plan.
Step 6
Correct the codes where they are configured
Change the value at its source rather than in the output: the language settings of your translation plugin or platform, the locale-to-hreflang mapping in your templates, the sitemap generator or the market configuration. If the CMS stores locales with underscores, convert them with a single mapping function used everywhere. Leave URL folders alone unless you are already planning a migration, because folder names do not have to match codes.
Step 7
Validate the output and lock it in
Purge caches, rerun the Hreflang Checker and confirm every value now passes, then view the
source of one page per locale to see the corrected tags as crawlers receive them. Add a format and meaning
check, like the script above, to your build or release process so a new market cannot go live with an
invalid code. Update the lang attribute at the same time if it had the same mistake.
Keeping codes correct as you add markets
- Keep one table that maps each market to its language, region, URL folder and hreflang value, and generate everything from it.
- When launching a new market, look up the codes in the ISO lists rather than guessing from the domain ending or the country name.
- Keep hreflang, the
langattribute andog:localederived from the same record, converting the separator forog:localeonly. - Review the value list whenever a translation plugin, platform or theme is updated, because defaults sometimes change.
For the wider picture of how codes fit into clusters, see the Hreflang Guide, and for choosing the fallback page for everyone outside your listed markets, see the x-default guide linked below.
1. Why is en-UK wrong when everyone says UK?
Hreflang region codes come from ISO 3166-1 alpha-2, and the code assigned to the United Kingdom there is GB, not UK. So the correct value for British English is en-GB. Google does not recognise en-UK, so a page labelled that way is effectively missing from the cluster. It is an easy mistake because UK is used everywhere else, including in domain names, and it also appears in older templates and plugin settings, so check every place the value is generated.
2. Are hreflang codes case sensitive?
No. Google reads en-gb, EN-GB and en-GB as the same value. The usual convention is a lower-case language and an upper-case region, which is how the ISO lists present them and how most documentation writes them. Following the convention does not change how Google treats the page, but it makes the source easier to read and makes errors such as en-UK or cz stand out more clearly when you or a colleague review the tags.
3. Can I use en_GB with an underscore?
Not in hreflang. The underscore format comes from software locale settings in systems such as WordPress, PHP and Java, and it is also the correct format for the Open Graph og:locale tag. Hreflang, like the HTML lang attribute, uses a hyphen, so the value must be en-GB. If your CMS stores locales with underscores, convert them in the template or use the plugin's separate hreflang field, rather than editing individual pages by hand.
4. Do I need a region code for every language?
No. Use a region only when the page is written for one country, for example because prices, delivery terms or spelling differ. A single French page for all French speakers should use plain fr. If you do split a language by country, consider also adding a language-only value that points at your most general version, so speakers of that language in countries you have not listed are still matched to an appropriate page.
5. Is there a hreflang code for Latin America or the EU?
Not in the format Google documents. Hreflang regions are individual countries from ISO 3166-1 alpha-2, so values such as es-LA, en-EU or es-419 should not be relied on. If one Spanish page serves all of Latin America, label it plain es. If you have separate pages for individual countries, give each its own value, such as es-MX or es-AR. For audiences outside all of your markets, use the x-default fallback.
6. How do I target Traditional and Simplified Chinese?
You can separate them by script, using zh-Hant for Traditional characters and zh-Hans for Simplified, or by country, using values such as zh-TW, zh-HK and zh-CN. Both are accepted. Choose the approach that matches how your content really differs: if the difference is only the script, script subtags describe it best; if prices, products or legal details change by market, country values are clearer. Use one approach consistently within each cluster.
7. What happens to a page with an invalid hreflang code?
Google ignores the annotation with the invalid value, so that version drops out of the cluster. The page can still be indexed and can still rank, but Google no longer knows it is the alternate for that audience, so searchers may be shown a sibling version instead. If the invalid value is on every page in the locale, the whole locale is effectively unannotated. No warning appears in Search Console, so a crawl-based check is needed to find it.
The Hreflang Checker follows each cluster of locale pages and validates return tags, language codes, x-default, sitemap consistency, target reachability and canonical alignment.
Open Hreflang Fixes โ