Strategy
International SEO: The hreflang Architecture That Actually Works
How to structure a multilingual site so each market ranks in its own language: URL architecture, hreflang rules, and the mistakes that break it.
Most multilingual sites do not fail because of translation quality. They fail because of architecture — and the failure is invisible from inside the company, because every employee sees the site correctly in their own browser.
Here is the single most common version of the failure:
The site "supports" six languages by swapping text with JavaScript on one URL.
There is one page. There is one URL. Google indexes it once, in one language. The German content exists only in the browser, after a script runs. It has never been indexed, because there is no German URL to index. A German buyer searching in German will never find it.
If your language switcher changes the page without changing the URL, you do not have a multilingual site. You have a monolingual site with a client-side translation feature.
The three valid architectures
Google supports exactly three ways to signal language and region. Pick one and be consistent.
| Architecture | Example | Best for |
|---|---|---|
| Subdirectory | example.com/de/ | Almost everyone |
| Subdomain | de.example.com | Separate infrastructure per market |
| ccTLD | example.de | Strong local brand, big budget |
Subdirectories win for most businesses. They inherit the domain's existing authority, cost nothing extra, and are simple to host. A new ccTLD starts from zero authority and has to be built up as a separate site — that is a multi-year investment, justified only when local trust genuinely requires a local domain.
URL parameters (example.com/?lang=de) are not on this list. Google explicitly discourages them for language targeting. They are treated inconsistently, they are easy to generate duplicates with, and they make the hreflang network fragile.
Localise the slugs too
If you have committed to per-language URLs, go the whole way:
/en/services/web-design/
/de/leistungen/webdesign/
/tr/hizmetler/web-tasarim/
/fr/services/conception-web/
Not:
/en/services/web-design/
/de/services/web-design/
/tr/services/web-design/
The second version works, but it wastes the URL — one of the few on-page signals a search engine reads before rendering anything. A German user scanning results sees a German URL and a German title. Both matter for click-through.
The objection is always maintenance. That objection is only valid if you are hand-writing files. If your URLs come from a content model — a slug field per language — the cost is one line per page per language.
hreflang: the four rules that break sites
hreflang is conceptually simple and unforgiving in practice. Four rules cover almost every real-world failure.
1. It must be reciprocal
If the English page points to the German page, the German page must point back. If it does not, Google ignores the annotation entirely — not partially, entirely.
This breaks most often when a page exists in some languages but not others, and the generation logic is not symmetrical.
2. Every page must reference itself
Each page's hreflang set must include an entry pointing to itself. A page that lists its alternates but omits its own URL has an invalid set.
3. Use absolute URLs
<!-- correct -->
<link rel="alternate" hreflang="de" href="https://example.com/de/leistungen/" />
<!-- broken -->
<link rel="alternate" hreflang="de" href="/de/leistungen/" />
Relative URLs are not supported. This one fails silently.
4. Language codes must be valid
- Language only:
de,tr,fr— targets speakers of that language anywhere - Language plus region:
de-AT,en-GB,pt-BR— ISO 639-1 language, ISO 3166-1 Alpha-2 region - Region alone is invalid.
hreflang="at"does not mean Austria.atis not a language code. - Script variants where genuinely needed:
zh-Hans,zh-Hant
And one special value: x-default, for the page shown to users whose language you do not target. Point it at your primary-language version or a language selector.
Here is a correct set, on every one of the three pages:
<link rel="alternate" hreflang="en" href="https://example.com/en/services/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/leistungen/" />
<link rel="alternate" hreflang="tr" href="https://example.com/tr/hizmetler/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/services/" />
The canonical trap
This one silently destroys multilingual sites, and it is worth stating precisely.
Each language version must be its own canonical.
<!-- On /de/leistungen/ — correct -->
<link rel="canonical" href="https://example.com/de/leistungen/" />
<!-- On /de/leistungen/ — destroys the German site -->
<link rel="canonical" href="https://example.com/en/services/" />
The second version tells Google the German page is a duplicate of the English one and should not be indexed separately. Combined with hreflang annotations saying the opposite, you have given contradictory instructions — and canonical wins.
The instinct behind the mistake is understandable: the pages are translations of each other, so they feel like duplicates. They are not. Translations are distinct pages serving distinct queries.
Handling partial translations
Real projects do not translate everything at once. You launch in three languages, add five more over two quarters, and blog posts trail behind.
There is a right way to handle the gap.
Do not fill untranslated pages with English content at a /de/ URL and index them. You have created thin, duplicate content in a market you are trying to build, and it competes with your English page for the same query.
Do track translation completeness per page and:
- Serve the page with English fallback so nothing 404s and internal links stay intact
- Add
<meta name="robots" content="noindex, follow">while it is below your quality threshold - Exclude it from that language's sitemap
- Omit it from the hreflang set on the other language versions
- Remove all four of the above automatically once the translation lands
This is straightforward to automate if translation coverage is a computed property of your build rather than a manual checklist. We run this site that way: any page below 85% translated is generated, linked and reachable, but explicitly noindexed and absent from the sitemap until it crosses the threshold.
The result is that a half-finished language never competes in search, and finishing it requires no manual SEO step.
Per-language sitemaps
Once you pass a few hundred URLs, one sitemap becomes unwieldy. Split by language and use a sitemap index:
<!-- /sitemap.xml -->
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap><loc>https://example.com/sitemaps/sitemap-en.xml</loc></sitemap>
<sitemap><loc>https://example.com/sitemaps/sitemap-de.xml</loc></sitemap>
</sitemapindex>
You can also declare hreflang inside the sitemap using xhtml:link elements, as an alternative to HTML head tags. Both work; do not do both, or you will eventually have them disagree.
The practical benefit of splitting is diagnostic: Search Console reports indexation per sitemap, so you can see at a glance that German is at 90% indexed while French is at 40%, and investigate the right thing.
Sending visitors to the right language
Two approaches, and one of them causes problems.
Redirect at the root based on Accept-Language. A visitor hitting / is 302-redirected to /de/ if their browser announces German. Crawlers send no Accept-Language header, so they fall through to your default. This is Google-approved and works well.
Two conditions: use a 302, not a 301 — the redirect is conditional on the request, not permanent. And always give the user a visible way to override it, because plenty of people prefer a language other than their browser default.
Never redirect based on IP geolocation alone. A German speaker travelling in Japan does not want Japanese. A crawler in the US sees only the English site, which means your other languages may never be discovered. IP geolocation is the single most common cause of "Google will not index our other languages."
Diagnosing an existing site
If you inherited a multilingual site and suspect problems, check these six things in order:
- Does each language have its own URL? If not, stop — that is the whole problem.
- Is
curl -s https://example.com/de/ | grep "a German sentence"non-empty? If not, your content is client-side only. - Do canonicals point at themselves? Check three pages in each language.
- Is hreflang reciprocal? Search Console's International Targeting report shows "no return tags" errors.
- Is anything noindexed that should not be? A staging directive that shipped is depressingly common.
- Are all languages in Search Console? Compare indexed page counts per language sitemap.
Our free SEO audit tool reports the hreflang annotations, canonical and indexing directives found on any page, which covers items 3 through 5.
The summary
International SEO is mostly architecture, and architecture decisions are cheap before launch and expensive afterwards. Give every language a real URL, localise the slugs, keep hreflang reciprocal and self-referencing, let each version be its own canonical, and noindex what is not translated yet instead of publishing English at a foreign URL.
Get those five right and translation quality becomes the thing that determines your results — which is where the decision should sit.