Hreflang for Regional Variants and x-default
Within Hreflang Tag Generation, this guide covers the cases where hreflang matters most and goes wrong most often: regional variants of the same language, such as British and American English or Spanish for Spain and Mexico, and the x-default value for everyone else. It explains how to code each variant, when to add language-only entries, what x-default should point at and how to validate codes before they reach production.
Regional variants are nearly duplicate pages by nature. The UK and US pages for a product share almost all their text, differing in spelling, currency, delivery details and legal notes. Without hreflang, search engines see near-duplicates and pick one to show everywhere, so British users land on prices in dollars. With correct annotations, each region gets its own page. With incorrect codes, the annotations are ignored and the problem remains, silently.
The Problem
A retailer ran English sites for the US, UK and Australia and Spanish sites for Spain and Mexico. Its hreflang used en-us, en-uk, en-au, es-es and es-mx. The UK code was invalid, since the ISO code for the United Kingdom is GB, so the UK pages were effectively outside every cluster. There was no language-only English entry, so English speakers in Ireland, India or Canada were shown whichever variant the search engine chose, often the US site with dollar prices. x-default pointed at the US homepage from every page, not at the equivalent page, which some audits flagged as inconsistent.
How Regional Annotations Work
Codes. The value is a language code, optionally followed by a region code: en-GB, es-MX, pt-BR. Language codes are ISO 639-1, region codes ISO 3166-1 alpha-2. Case does not matter to search engines, but use the conventional form, lowercase language and uppercase region, to make mistakes easier to spot. Common invalid codes include en-UK (use en-GB), jp (use ja), cn for Chinese (use zh, with a script or region such as zh-CN, zh-TW or zh-Hant), and region-only values.
Language-only entries. For each language with regional variants, add one language-only entry pointing at the version that should serve other regions. en might point at an international English site, or at the US site if there is no international one.
x-default. The fallback for users matching no entry. Point it at a language or country selector if you have one, or at the default locale’s version of the same page. It should be the equivalent page, not always the homepage.
Same cluster everywhere. All of these entries, region codes, language-only entries and x-default, are part of the cluster and must appear identically on every member.
Implementation
Keep the mapping from site locales to hreflang codes, language-only designations and the x-default rule in configuration, validated at startup.
// lib/seo/hreflang-config.ts
export const HREFLANG = {
codes: { "en-us": "en-US", "en-gb": "en-GB", "en-au": "en-AU", "es-es": "es-ES", "es-mx": "es-MX", de: "de", ja: "ja" } as Record<string, string>,
languageDefaults: { en: "en-us", es: "es-es" } as Record<string, string>, // which site locale serves "en", "es"
xDefault: "en-us" as string, // or "selector" for a language picker page
};
const LANGS = new Set(["en", "es", "de", "ja", "fr", "pt", "zh", "it", "nl"]);
const REGIONS = new Set(["US", "GB", "AU", "ES", "MX", "DE", "JP", "FR", "BR", "PT", "CN", "TW", "IT", "NL", "CA", "IE"]);
export function validateHreflangConfig() {
for (const [locale, code] of Object.entries(HREFLANG.codes)) {
const [lang, region] = code.split("-");
if (!LANGS.has(lang)) throw new Error(`Invalid language in ${code} for ${locale}`);
if (region && region.length === 2 && !REGIONS.has(region)) throw new Error(`Invalid region in ${code} for ${locale}`);
}
}
The cluster builder adds language-only entries and x-default from this configuration, using the path of the same entry in the designated locale.
// lib/seo/hreflang.ts (excerpt)
export function withRegionalExtras(alternates: { locale: string; href: string }[]) {
const out = alternates.map((a) => ({ hreflang: HREFLANG.codes[a.locale], href: a.href }));
for (const [lang, locale] of Object.entries(HREFLANG.languageDefaults)) {
const target = alternates.find((a) => a.locale === locale);
const hasOtherVariant = alternates.some((a) => a.locale !== locale && HREFLANG.codes[a.locale]?.startsWith(`${lang}-`));
if (target && hasOtherVariant) out.push({ hreflang: lang, href: target.href });
}
const xd = alternates.find((a) => a.locale === HREFLANG.xDefault);
if (xd) out.push({ hreflang: "x-default", href: xd.href });
return out;
}
The language-only entry is added only when the hreflang set contains more than one variant of that language; for a page that exists only in US English, en-US alone is enough.
Making regional variants genuinely regional
Hreflang tells search engines which page belongs to which region, but pages that are character-for-character identical may still be folded together. Make regional variants reflect the region: local spelling, currency and prices, delivery and returns information, contact details, legal notices and local examples. These differences also serve readers, which is the point. When a variant has nothing regional about it, consider whether it needs to exist; a single international English page with hreflang en is often better than three identical regional copies.
Choosing the language-only target
The language-only entry decides where speakers of a language in unlisted regions land: English speakers in India, Spanish speakers in Argentina. Choose the variant that serves them best, not the largest market by default. An international variant with neutral spelling, a currency selector and global shipping information is ideal; if none exists, pick the variant whose terms, such as shipping and payment options, are most broadly applicable. Review the choice with regional traffic data, which shows where readers of unlisted regions actually come from.
Configuration Reference
| Item | Recommendation | Why |
|---|---|---|
| Codes | ISO 639-1 language, ISO 3166-1 region | Invalid codes are ignored silently. |
| Language-only entry | one per language with several variants | Serves other regions of that language. |
| x-default | selector page or equivalent default-locale page | Catches unmatched users. |
| Validation | at startup and in CI | Typos never reach production. |
| Variant content | genuinely regional | Near-duplicates may be folded together. |
| Cluster | identical on every member | Reciprocity. |
Gotchas & Edge Cases
- Lowercase locale paths. Paths like
/en-gb/are fine; the hreflang value is separate and should useen-GB. Do not derive codes by string manipulation of paths. - Chinese variants. Use script subtags (
zh-Hans,zh-Hant) or regions (zh-CN,zh-TW,zh-HK) consistently; mixing them within one cluster confuses audits. - x-default on the selector page. A language selector page that is
x-defaultshould itself carry the cluster of the homepages, since it is the homepage’s equivalent. - Geo-redirects. Automatically redirecting by IP prevents crawlers from reaching regional versions. Use hreflang and a suggestion banner instead of forced redirects.
Worked Example
The retailer corrected en-uk to en-GB, added en pointing at a new international English site and es pointing at the Spain site, and changed x-default to point at the equivalent page on the international site. It also added regional details, such as delivery times and prices in local currency, to product pages that had been identical across English variants. Within two months, UK product impressions in search shifted from the US pages to the UK pages, and Irish and Indian English searchers began landing on the international site with a currency selector.
Deciding on Regional Variants at All
Regional variants multiply content, translation and maintenance work, so they should exist only where they help readers. The strongest reasons are commercial and legal: different prices, currencies, product ranges, delivery terms or regulations. Spelling differences alone rarely justify separate sites; many international brands serve one English site for all regions, with local currency and delivery details handled dynamically, and annotate it with plain en. Where you do create variants, give each an owner in the region and a reason to differ, and review annually whether the variant still earns its keep. Removing a variant later is possible, with redirects to the surviving version and a cluster update, but it is easier to start with fewer.
Rollout Checklist
- Map every site locale to a valid hreflang code in configuration.
- Validate codes at startup and in CI.
- Add language-only entries for languages with several regional variants.
- Point x-default at a selector or the equivalent default-locale page.
- Make regional variants genuinely regional in content.
- Avoid forced geo-redirects; use suggestion banners.
Frequently Asked Questions
Is hreflang case-sensitive?
No, but consistent formatting, lowercase language and uppercase region, makes review easier.
Can one page serve several regions?
Yes, with a language-only code, or by listing the same URL under several regional codes. Each region code can appear only once per cluster.
Must x-default be a separate page?
No. It can point at an existing member, such as the international English page, which then appears twice in the cluster.
Do we need hreflang if variants are on separate country domains?
Yes. Country domains give a geographic signal, but hreflang still clarifies which pages are equivalents across the domains.