Module 041 Advanced 19 min read

International SEO (hreflang)

Multi-language vs multi-region, ccTLD/subdomain/subfolder trade-offs, hreflang implementation in HTML, sitemap, and HTTP, plus x-default.

By SEO Mastery Editorial

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-default is 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’s Accept-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:

  1. 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-FR and fr-CA region variants of the same language. The URL pattern, content strategy, and hreflang setup all hinge on this answer.
  2. 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).
  3. 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:

ArchitectureExampleProsConsBest for
Subfolders on gTLDexample.com/de/ example.com/es/One authority, one technical stack, easy hreflangNo local-domain signalDefault choice; SaaS, content, mid-size e-commerce
Subdomainsde.example.com es.example.comEasy regional infrastructure (separate CDN, currency)Authority does not always flow between subdomainsLocal-currency e-commerce, separate teams
ccTLDsexample.de example.esStrongest local signal, legally distinctEach domain starts at zero authority, expensive to maintainBrands legally required to operate locally; banks, telcos, government
Parameter-basedexample.com/?lang=deEasiest to implementGoogle ignores parameters as locale signal; never useNone — 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 validhreflang="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) and pt-PT (correct) — both required to differentiate Brazilian and European Portuguese

Three valid hreflang implementations, in order of preference for most sites:

  1. HTML <link> tags in <head> — works for any HTML page, easiest to maintain in a templated site.
  2. XML sitemap <xhtml:link> — best for very large multi-locale sites because it consolidates the entire hreflang map in one place.
  3. 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-DE variant; page B does not list A at all. Google ignores the entire cluster.
  • Wrong region code. en-UK instead of en-GB, de-AT written as at-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/de vs example.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:// vs https:// 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

  1. 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.”
  2. 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.
  3. 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.
  4. Decide on architecture: subfolders, subdomains, or ccTLDs. Document the decision and do not change it later — migrating between architectures costs months of ranking volatility.
  5. 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.
  6. 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.
  7. Pick one method: HTML <link> tags for sites under ~10k pages; XML sitemap xhtml:link for larger. Never mix. Document which method is in use in your engineering wiki.
  8. Add x-default to 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.
  9. Localize currency, date format, phone format, and address format alongside language. 1,299.99 vs 1.299,99 vs 1 299,99 are real ranking signals because they correlate with which audience you actually serve.
  10. 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

View all on the home page →
  1. 026 Technical SEO Fundamentals 12m
  2. 027 Site Architecture 20m
  3. 028 Crawling & Indexing 17m
  4. 029 robots.txt Deep Dive 15m
  5. 030 XML Sitemaps 12m
  6. 031 Canonical Tags 20m
  7. 032 Meta Robots & X-Robots-Tag 13m
  8. 033 HTTP Status Codes 15m
  9. 034 Crawl Budget Management 16m
  10. 035 JavaScript SEO 26m
  11. 036 Core Web Vitals 17m
  12. 037 Site Speed & Performance 19m
  13. 038 HTTPS & Site Security 12m
  14. 039 Mobile SEO & Mobile-First Indexing 14m
  15. 040 Structured Data & Schema Markup 17m
  16. 041 International SEO (hreflang) You're here 19m
  17. 042 Pagination 12m
  18. 043 Faceted Navigation 26m
  19. 044 Duplicate Content 13m
  20. 045 Site Migrations 24m