The structure question comes up once, before the second language exists, and it is expensive to change later. This guide puts the three options side by side with what each costs in hosting, authority and admin, says which signals Google really uses to decide who sees which version, and then covers the part that is the same whatever you choose: hreflang, canonicals and the checks that prove they work. Reading it takes ten minutes; the decision usually follows from your answer to one question at the end of the table.
Quick answer
| Structure | Example | Authority | Setup and running cost | Country signal |
|---|---|---|---|---|
| Subfolder | example.com/de/ | One domain, shared | One host, one certificate, one CMS; easiest | Only through hreflang |
| Subdomain | de.example.com | Treated much like a separate site in practice | Separate hosting possible; DNS and certificates per host | Only through hreflang |
| ccTLD | example.de | Separate per domain, built from zero | A domain, a registration, often a legal presence per country | Strongest: the TLD itself |
- A small site translating its content: subfolders.
- A company with national teams, separate stock, prices or legal entities: ccTLDs, or subfolders per country if the marketing is central.
- SaaS and content sites selling the same thing everywhere: subfolders.
- Whatever the structure, every version needs a self-referencing canonical and a complete hreflang set with return links. Run the hreflang checker on one page per template.
Why the structure matters
Three things depend on it. Authority: links to example.com/de/ count for the whole domain, links to example.de count for example.de alone, and a new ccTLD starts with no history at all. Cost and admin: one domain means one certificate, one CMS, one deployment; a ccTLD per market means a registration each, sometimes a local address to qualify for it, and a separate site to keep patched. Targeting: a ccTLD tells Google the country without further work, while subfolders and subdomains tell it nothing until hreflang does.
The mistakes are symmetrical. A company that runs eight ccTLDs for eight small markets spends years building eight reputations for one product. A company that stuffs six countries with different prices, currencies and legal terms into one domain without hreflang shows Austrians German prices and Swiss visitors euros.
What Google uses to decide the country
Google's documentation on managing multi-regional and multilingual sites lists the signals in this order: the country-code top-level domain, hreflang annotations, server location, and other signals such as local addresses and phone numbers, the currency, and links from local sites. Two of those need a comment.
Server location is listed as a weak signal that Google tries not to rely on, because content delivery networks put servers everywhere. In practice it decides nothing; host wherever is fastest for your visitors.
Search Console's International Targeting report, where you could once set a country for a .com, was removed in 2022. There is no longer a switch; the signals above are all there is. If you read a guide that tells you to "set the geographic target in Search Console", it is out of date.
Currency, addresses and the language of the page itself are signals you control on every structure. A /de/ folder with prices in CHF, a Zurich address and lang="de-CH" is targeted at Switzerland as clearly as a .ch domain, once hreflang says so.
Language versus country
de is a language. de-AT and de-CH are a language in a country. Most sites need language versions, not country versions: a German page serves Germany, Austria and Switzerland unless the prices, the laws, the delivery options or the vocabulary differ. When they do, you need a page per country, and that is a business decision (do we sell there, at what price, under whose terms) before it is a technical one.
The technical rule is simple: one page per combination that is actually different. Two identical pages tagged de-DE and de-AT are duplicate content wearing different badges; Google will fold them, and the fold may not go the way you wanted. If the content is the same, publish one page tagged de.
How getReport checks it
The checker reads every <link rel="alternate" hreflang> on the page, validates the codes and the set, then fetches up to 50 of the alternates (whatever domain they are on) and reads their own hreflang tags to confirm the return link. The table lists each alternate with its code, URL, HTTP status, whether it links back, and the lang attribute of its <html> element:

Two details worth knowing. The self-reference counts as present if any entry in the set points at the page's own URL or at its canonical, so a page reached under a variant URL still passes when its canonical is in the set. And the return link is checked against the same pair: the alternate must list this page's URL or its canonical. That is exactly the leniency Google shows, and no more.
The html lang column is the fastest way to catch a structure mistake: a de.example.com row whose html lang reads en is a subdomain that was set up but never translated, and Google will notice the same thing.
Step by step
1. Decide with the questions, not with a preference
Work down the list; the first "yes" usually settles it.
- Do the countries have separate legal entities, stock, prices or teams that publish on their own schedule? → ccTLDs, or subfolders per country under one central CMS if marketing is central and the differences are a price list.
- Is there an existing ccTLD with years of links and rankings? → Keep it; add hreflang to connect it to the rest.
- Is it one product, one team, translated content? → Subfolders.
- Do you need a separate technical stack for one market (a different CMS, a partner's platform)? → A subdomain for that market, subfolders for the rest, hreflang across both.
Subdomains are rarely the first choice. Google has said for years that it handles both, but subdomains get treated much like separate sites in practice, and they cost the DNS, certificate and deployment work of separate sites without the country signal of a ccTLD. They are the right answer when a market needs its own stack.
2. Put hreflang on every version, across whatever domains you use
hreflang works across subfolders, subdomains and domains alike; the set just lists full URLs. A German page on a ccTLD structure carries:
<link rel="alternate" hreflang="de" href="https://example.de/schuhe/" />
<link rel="alternate" hreflang="de-AT" href="https://example.at/schuhe/" />
<link rel="alternate" hreflang="de-CH" href="https://example.ch/schuhe/" />
<link rel="alternate" hreflang="en" href="https://example.com/shoes/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/" />
<link rel="canonical" href="https://example.de/schuhe/" />The same five alternate lines appear on all four pages; only the canonical changes. x-default names the page for visitors whose language you do not serve: a language chooser, or the main market's page. x-default explained covers when to use which.
For a site with many templates, generate the set from one routing table rather than typing it per page. In the XML sitemap the same set is written once per URL:
<url>
<loc>https://example.de/schuhe/</loc>
<xhtml:link rel="alternate" hreflang="de" href="https://example.de/schuhe/" />
<xhtml:link rel="alternate" hreflang="de-AT" href="https://example.at/schuhe/" />
<xhtml:link rel="alternate" hreflang="de-CH" href="https://example.ch/schuhe/" />
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/shoes/" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/" />
</url>With ccTLDs, each domain's sitemap can only list its own URLs as <loc>, but the xhtml:link entries may point anywhere; every domain must be verified in the same Search Console account for Google to use them. hreflang in sitemaps vs in HTML weighs the two.
3. Keep every canonical on its own version
Each language or country page canonicalises to itself. A canonical from example.at/schuhe/ to example.de/schuhe/ tells Google to drop the Austrian page, and no amount of hreflang brings it back. The one exception is a true duplicate you do not want indexed separately, and then it should not be in the hreflang set either.
4. Set the lang attribute per version
<html lang="de-AT"> on the Austrian page, <html lang="en"> on the English one. Screen readers use the lang attribute for pronunciation; the checker's table shows it per alternate; a mismatch between lang and the hreflang code is the quickest sign that a version serves the wrong content. In WordPress the theme prints it from Settings → General → Site Language, and multilingual plugins set it per language.
5. Do not redirect by IP without a way out
A visitor in Austria opening example.de may want the German site; an Austrian in Germany, a traveller, or Googlebot (which mostly crawls from the United States) certainly do not want to be bounced elsewhere. Googlebot that is redirected on every request never sees the page it asked for, so the alternates it fetches for the return-link check are not the pages you tagged. Show a banner ("It looks like you are in Austria. Switch to example.at?") and remember the choice in a cookie; never a forced redirect. If you must redirect, exempt known crawlers and always keep every version reachable at its own URL.
6. Check one page per template
The home page, a category, a product or article, a contact page. Paste each into the checker and read the table: every row should show a 200 status, "yes" in the return-link column and the expected html lang. Then read the findings: valid codes, one x-default, self-reference present. Hreflang return links explains each failure.
Moving between structures
Changing from ccTLDs to subfolders, or the other way, is a site migration per country: every URL on the old host gets a permanent redirect to its equivalent on the new one, the hreflang sets on every version are rewritten to the new URLs on the same day, the sitemaps are regenerated, and Search Console's Change of Address is filed for each domain that moves. Consolidating eight ccTLDs into one domain typically loses some rankings for months while Google re-evaluates and gains them back with interest once the authority is pooled; splitting a domain into ccTLDs loses more and gains it back slower. Plan it with the site migration checklist, and do not do it for a speed or a "signal" reason alone.
Speed for far-away markets
A ccTLD does not make a site faster; a server or an edge cache near the visitor does. One origin with a CDN in front serves Sydney from Sydney and São Paulo from São Paulo whatever the domain says, and for cached pages the time to first byte is the same on all three structures. Only uncached, personalised pages (a logged-in account, a cart) benefit from an origin in the region, and that is a hosting decision, not a domain one. CDN basics covers what the edge does and does not fix.
Platform notes
WordPress
Three routes. A multisite network gives each language its own site under a subfolder or subdomain (or a mapped domain), with separate plugins and themes to keep in step. WPML and Polylang run all languages in one install and let you choose the URL format: language in a folder, on a subdomain, or a different domain per language. The plugin writes the hreflang set and the lang attribute; you configure the mapping once. Which to pick, and the settings that break return links, are in Multilingual WordPress: WPML, Polylang and hreflang.
Shopify
Shopify Markets publishes each market under a subfolder (example.com/en-de/), a subdomain, or a separate domain you connect, and writes the hreflang tags itself. The choice is per market, so a shop can run subfolders for most markets and a ccTLD for one that already has authority.
Webflow, Wix and Squarespace
Webflow Localization and Wix Multilingual put each language in a subfolder and generate hreflang; neither offers ccTLDs from inside the product. Squarespace has no built-in multilingual mode; the workable pattern is one site per language, which means separate domains or subdomains and hand-written hreflang in the code injection settings.
Verify
- The hreflang checker shows every alternate with status 200, return link "yes" and the expected
html lang, on every template. - Every version's canonical points at itself; the canonical checker shows "self-referencing" on each.
- In Search Console, each property (folder, subdomain or domain) shows the intended versions in the performance report for the intended countries. If Germany keeps seeing the Austrian page, the return links are broken somewhere.
- No forced IP redirect: opening any version through a VPN in another country shows that version, with a banner at most.
Common mistakes
- Redirecting by IP with no way out. Visitors and crawlers cannot reach the version they asked for. Suggest, do not force.
- Machine translations left indexable. Ten auto-translated languages of thin quality make the whole domain look thin. Translate what you can support; keep the rest unpublished or
noindex. - Flags for languages. A Swiss flag next to "Deutsch" tells a German visitor this is not for them. Name the language in its own script ("Deutsch", "Français"); use flags only for countries.
- Mixed structures with no map.
/de/for German,fr.example.comfor French andexample.itfor Italian, each set up by a different agency, with hreflang on none. Any mix works if one routing table generates every set. - A canonical to the source language. The translated page disappears from search, and the team blames hreflang.
- The same page tagged with several countries.
de-DE,de-ATandde-CHall pointing at one URL is allowed, but pointless if the page is identical; tag itdeonce.