International SEO (hreflang)
Multi-language vs multi-region, ccTLD/subdomain/subfolder trade-offs, hreflang implementation in HTML, sitemap, and HTTP, plus x-default.
International SEO is where the most expensive technical mistakes live. The wrong URL architecture is a 12-month mistake; broken hreflang silently cannibalizes regional rankings for years; missing x-default sends Brazilian visitors to your German page. The good news: hreflang is one of the few SEO disciplines where the right answer is unambiguous if you accept the constraints. This module is the constraint-first playbook.
TL;DR
- Pick architecture before content: ccTLD, subdomain, or subfolder. Subfolders win for most modern sites because authority consolidates on one domain; ccTLDs only beat subfolders when local trust matters more than crawl economy.
- Hreflang must be reciprocal. If A points to B, B must point back to A — and to itself. Missing reciprocity is the single most common cause of hreflang being silently ignored.
x-defaultis for users without a matching language, not the English version. It signals the fallback locale Google should serve when no other variant matches the user’sAccept-Language.
The mental model
International SEO is like running a chain of bookstores in different cities. Each store has its own inventory (localized content), its own street address (URL), and its own staff who speak the local language. Hreflang is the directory at the entrance of every store that says “this title is also stocked in our Berlin branch (German), Madrid branch (Spanish), and Singapore branch (English-Singapore).”
Three questions decide everything else:
- Does language differ across stores, or does region differ, or both? A French page for France and a French page for Canada are different
fr-FRandfr-CAregion variants of the same language. The URL pattern, content strategy, and hreflang setup all hinge on this answer. - Where do you want each store’s signal to compound? ccTLDs are independent buildings on different streets; subfolders are floors of one building. Building one tall structure (subfolders) is almost always faster than building twenty small ones (ccTLDs).
- What does the directory at the entrance look like? That’s hreflang. Get the directory wrong and customers get sent to the wrong store; get it right and they always land where they belong.
Deep dive: the 2026 reality
Architecture options, ranked for most modern teams:
| Architecture | Example | Pros | Cons | Best for |
|---|---|---|---|---|
| Subfolders on gTLD | example.com/de/ example.com/es/ | One authority, one technical stack, easy hreflang | No local-domain signal | Default choice; SaaS, content, mid-size e-commerce |
| Subdomains | de.example.com es.example.com | Easy regional infrastructure (separate CDN, currency) | Authority does not always flow between subdomains | Local-currency e-commerce, separate teams |
| ccTLDs | example.de example.es | Strongest local signal, legally distinct | Each domain starts at zero authority, expensive to maintain | Brands legally required to operate locally; banks, telcos, government |
| Parameter-based | example.com/?lang=de | Easiest to implement | Google ignores parameters as locale signal; never use | None — actively harmful |
Language vs region targeting. Hreflang values use ISO 639-1 language codes optionally followed by ISO 3166-1 alpha-2 region codes: en, en-US, en-GB, de, de-CH, es-419 (Latin American Spanish, special case). Region without language is not valid — hreflang="us" is invalid; hreflang="en-US" is correct. The most common wrong codes:
en-UK(wrong) →en-GB(correct)zh-CN(deprecated form, still works) →zh-Hans(Simplified Chinese, recommended)pt-BR(correct) andpt-PT(correct) — both required to differentiate Brazilian and European Portuguese
Three valid hreflang implementations, in order of preference for most sites:
- HTML
<link>tags in<head>— works for any HTML page, easiest to maintain in a templated site. - XML sitemap
<xhtml:link>— best for very large multi-locale sites because it consolidates the entire hreflang map in one place. - HTTP
Link:headers — only option for non-HTML resources (PDFs).
Pick one method per URL — never mix. Mixing HTML and sitemap hreflang for the same page is the second-most-common cause of silent ignore.
x-default is a hreflang value, not a separate attribute. It marks the fallback URL Google should serve when no other variant matches. The correct use:
<link rel="alternate" hreflang="en-US" href="https://example.com/" />
<link rel="alternate" hreflang="de-DE" href="https://example.com/de/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />
x-default is not “the English version.” It is “the version to serve when no language match exists” — typically a country selector or the most universal localization. For a true fallback, point x-default to a /select-region/ page or your default locale.
The 2024–2026 hreflang failure modes still showing up in audits:
- Non-reciprocal links. Page A claims B as
de-DEvariant; page B does not list A at all. Google ignores the entire cluster. - Wrong region code.
en-UKinstead ofen-GB,de-ATwritten asat-DE. Google ignores invalid codes silently. - URL mismatch with canonical. The hreflang URLs do not match the canonical of the target page. Google trusts canonical, ignores hreflang.
- Trailing slash inconsistency.
example.com/devsexample.com/de/listed differently across pages. - Hreflang to redirected URLs. A claims B at
/old-de/, but/old-de/301s to/de/. Google follows the redirect but distrusts the cluster. - Mixed protocol.
http://vshttps://in hreflang values after a HTTPS migration.
International CDN setup matters more than people think. A Brazilian user hitting example.com/pt-br/ on a US-only origin gets 700ms TTFB and falls out of the Good CrUX bucket. Cloudflare, Fastly, and AWS CloudFront have global edges that solve this for free. Currency, date format, and culturally specific content must localize alongside language: R$ 1.299,99 (Brazilian real with comma decimal) is not optional — it is what tells Google you really serve Brazil, not just that you translated to Portuguese.
The AI search systems (ChatGPT Search, Perplexity, Google AI Overviews, Gemini) honor hreflang by indexing the variant most likely to satisfy the user’s locale. Bad hreflang means a German query may surface your English page in ChatGPT (which uses Bing’s index) — a worse experience than no localization at all.
Visualizing it
flowchart TD
A[User in Germany searches in German] --> B[Google detects geo + language]
B --> C{Site has hreflang cluster for this query?}
C -->|Yes, valid + reciprocal| D[Serve de-DE variant]
C -->|Invalid or one-way links| E[Google ignores hreflang, picks by canonical]
C -->|No hreflang, single en page| F[Serve en page, may rank lower locally]
D --> G[User lands on localized page, low bounce]
E --> H[User may land on en page, higher bounce]
I[hreflang map] -.feeds.-> C
Bad vs. expert
The bad approach
<!-- Page: example.com/de/produkt -->
<head>
<title>Produkt</title>
<link rel="canonical" href="https://example.com/de/produkt/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/produkt" />
<link rel="alternate" hreflang="en-UK" href="https://example.com/en/product/" />
<link rel="alternate" hreflang="us" href="https://example.com/us/product/" />
</head>
<!-- And a separate hreflang block in the sitemap for the same URL -->
<url>
<loc>https://example.com/de/produkt/</loc>
<xhtml:link rel="alternate" hreflang="de-DE" href="https://example.com/de/produkt/" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/product/" />
</url>
Five problems in nine lines. Canonical has trailing slash, de hreflang doesn’t — Google sees a mismatch and ignores. en-UK is invalid (should be en-GB). us is invalid as a hreflang value (region without language). HTML and sitemap declarations conflict. There is no x-default. There is no self-referencing hreflang on this page (a page must list itself in its own hreflang cluster). The en page also won’t list this page back. Google ignores the cluster entirely; the German page competes with the English page for German queries and usually loses.
The expert approach
<!-- Page: example.com/de/produkt/ — note trailing slash matches canonical -->
<head>
<title>Produkt</title>
<link rel="canonical" href="https://example.com/de/produkt/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/product/" />
<link rel="alternate" hreflang="en-US" href="https://example.com/en-us/product/" />
<link rel="alternate" hreflang="en-GB" href="https://example.com/en-gb/product/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/produkt/" />
<link rel="alternate" hreflang="de-AT" href="https://example.com/de-at/produkt/" />
<link rel="alternate" hreflang="de-CH" href="https://example.com/de-ch/produkt/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/produit/" />
<link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-br/produto/" />
<link rel="alternate" hreflang="zh-Hans" href="https://example.com/zh-hans/产品/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/select-region/" />
</head>
<!-- For a 50,000-page multi-locale site, prefer the sitemap form -->
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/de/produkt/</loc>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/produkt/" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/product/" />
<xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-br/produto/" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/select-region/" />
</url>
<url>
<loc>https://example.com/en/product/</loc>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/produkt/" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/product/" />
<xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-br/produto/" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/select-region/" />
</url>
</urlset>
# For non-HTML files (PDFs, RSS), use HTTP Link headers
HTTP/1.1 200 OK
Content-Type: application/pdf
Link: <https://example.com/de/whitepaper.pdf>; rel="alternate"; hreflang="de",
<https://example.com/en/whitepaper.pdf>; rel="alternate"; hreflang="en",
<https://example.com/select-region/whitepaper>; rel="alternate"; hreflang="x-default"
Every page lists itself plus every variant. Codes are valid (zh-Hans for Simplified Chinese, pt-BR for Brazilian Portuguese, en-GB for UK English). URLs match canonicals exactly, including trailing slashes. x-default points to a region selector, not the English page. The choice between HTML and sitemap is exclusive — pick one method per URL. HTTP headers are reserved for non-HTML resources.
Do this today
- Open Google Search Console → Legacy tools and reports → International Targeting. The Language tab lists every hreflang error Google has detected. No data here means hreflang isn’t being detected, not “everything is fine.”
- Run Screaming Frog SEO Spider with Configuration → Spider → Crawl → Hreflang enabled. The Hreflang tab lists missing return links, invalid codes, mismatched canonicals, and inconsistent URLs across the cluster.
- Validate one cluster manually with the Hreflang Tags Testing Tool at
merkle.com/seo-toolbox/hreflang.php. Paste any URL; it crawls every variant and shows the reciprocal link matrix. - Decide on architecture: subfolders, subdomains, or ccTLDs. Document the decision and do not change it later — migrating between architectures costs months of ranking volatility.
- In Google Search Console, submit a separate property for each subfolder (
example.com/de/,example.com/fr/) so you can monitor Search performance per locale. - For each page, write the canonical URL once and derive every hreflang URL from a config table (locale → URL pattern). This is the only way to keep reciprocity intact across thousands of pages.
- Pick one method: HTML
<link>tags for sites under ~10k pages; XML sitemapxhtml:linkfor larger. Never mix. Document which method is in use in your engineering wiki. - Add
x-defaultto every cluster. Point it at a region-selection page (/select-region/) or your most universal locale, not your English page unless English is genuinely your fallback. - Localize currency, date format, phone format, and address format alongside language.
1,299.99vs1.299,99vs1 299,99are real ranking signals because they correlate with which audience you actually serve. - Re-test in Search Console → International Targeting two weeks after any change. The report updates slowly; expect a 7-14 day lag between fixing reciprocity and seeing the cluster validated.
Mark complete
Toggle to remember this module as mastered. Saved to your browser only.
More in this part
Part 5: Technical SEO
- 026 Technical SEO Fundamentals 12m
- 027 Site Architecture 20m
- 028 Crawling & Indexing 17m
- 029 robots.txt Deep Dive 15m
- 030 XML Sitemaps 12m
- 031 Canonical Tags 20m
- 032 Meta Robots & X-Robots-Tag 13m
- 033 HTTP Status Codes 15m
- 034 Crawl Budget Management 16m
- 035 JavaScript SEO 26m
- 036 Core Web Vitals 17m
- 037 Site Speed & Performance 19m
- 038 HTTPS & Site Security 12m
- 039 Mobile SEO & Mobile-First Indexing 14m
- 040 Structured Data & Schema Markup 17m
- 041 International SEO (hreflang) You're here 19m
- 042 Pagination 12m
- 043 Faceted Navigation 26m
- 044 Duplicate Content 13m
- 045 Site Migrations 24m