← All posts

Hreflang and international URL structure, a practical guide


Hreflang is for sites with equivalent pages in more than one language or for more than one region, and it matters most when versions share a language, such as US and UK English. It tells Google which URL to show to which searcher, so it won't lift rankings or rescue pages that aren't true equivalents, and it only works when every page in a set confirms every other page.

Two decisions come before any markup: whether you need separate versions at all, and which URL structure they live on. Getting either wrong creates work that hreflang can't undo.

Decide whether you need separate versions

Your situationSeparate URLs?Hreflang?
One language, one offer for everyoneNoNo
Same language, different prices, delivery, spelling or legal terms (en-US, en-GB, en-AU)YesYes, this is where it helps most
Full translations of the same pagesYesRecommended
Main content in one language, only navigation and footer translatedUsually already the caseRecommended by Google
Different content per language, not translationsYesNo, the pages aren't alternates
Some pages translated, others notFor the translated pagesOnly between pages that have an equivalent

Three details shape that table. Google's guide to localized versions says it doesn't use hreflang or the HTML lang attribute to detect a page's language; it works the language out from the content. Its international site guidance adds that it uses visible content, so each page should use one language for content and navigation, without side-by-side translations. And hreflang connects equivalents: a German article that covers a different topic from its English neighbor gets no annotation linking the two.

Partial translation is common and manageable. Google says that if a complete set of links is hard to maintain, you can leave some languages out on some pages, and it will still process the pairs that point to each other.

Redirecting visitors by location can undermine all of this. Google's international guidance says to avoid automatically redirecting users between language versions, and notes that most Google crawls originate from the US without varying location, so a crawler redirected by IP may never reach the other versions. A banner suggesting the local site, plus a visible language selector, gives visitors the right version without hiding anything from crawlers.

Choose the URL structure

Google's international guidance lays out the trade-offs of each structure. The right-hand column is a judgment based on those trade-offs, not Google's wording.

StructureExampleGoogle's prosGoogle's consUsually fits
Country domainexample.deClear geotargeting, easy separationExpensive, more infrastructure, one country per domainBrands that need a strong local presence and can run separate sites
Subdomainde.example.comEasy setup, different server locations possibleUsers can't tell if "de" means language or countryMarkets run by separate teams or stacks
Subfolderexample.com/de/Easy setup, low maintenance on one hostSingle server location, harder to separateMost sites with one codebase
URL parameterexample.com?loc=deNone listedNot recommended by GoogleAvoid

Two background facts matter here. Google treats some vanity country domains, such as .tv and .me, as generic. And Search Console no longer has a country targeting setting: Google deprecated the International Targeting report in 2022 while confirming it still supports hreflang. On a generic domain, then, hreflang and the content itself (currency, delivery details, local contact information) carry the regional signal.

Changing structure later means migrating every international URL, so choose with a few years in mind. If you do have to move, treat it like any other move, using the site migration checklist.

How a hreflang set works

The rules Google documents for a set, plus one practical addition at the end:

  1. Every version lists itself and every other version. A page that lists only its siblings is incomplete.

  2. Links must be returned. If page A names page B and B doesn't name A, Google ignores the annotation. That rule exists so another site can't declare itself an alternate of your pages.

  3. URLs must be fully qualified, including https. A path like /de/page is not enough.

  4. The language comes first as an ISO 639-1 code, optionally followed by an ISO 3166-1 Alpha 2 region code. A script can be added in ISO 15924 format, such as zh-Hant.

  5. Alternates can sit on different domains.

  6. x-default is optional. Google describes it as the fallback for languages you haven't listed and suggests it especially for language selectors and home pages that redirect automatically.

  7. If you have several regional versions in one language, Google suggests a generic language tag as a catch-all for that language's other speakers. It can point at one of the regional pages.

  8. Every URL in the set should be the canonical version, return 200 and be indexable.

Markup in the head

Here is a hypothetical set for a running shoe category in US English, UK English, Spain Spanish and Mexico Spanish. Every page in the set carries the identical block, plus its own self-referencing canonical:

<link rel="alternate" hreflang="en-US" href="https://www.example.com/en-us/running-shoes/">
<link rel="alternate" hreflang="en-GB" href="https://www.example.com/en-gb/running-shoes/">
<link rel="alternate" hreflang="en" href="https://www.example.com/en-us/running-shoes/">
<link rel="alternate" hreflang="es-ES" href="https://www.example.com/es-es/zapatillas-running/">
<link rel="alternate" hreflang="es-MX" href="https://www.example.com/es-mx/tenis-para-correr/">
<link rel="alternate" hreflang="es" href="https://www.example.com/es-mx/tenis-para-correr/">
<link rel="alternate" hreflang="x-default" href="https://www.example.com/en-us/running-shoes/">

The plain en and es tags catch English speakers outside the US and UK and Spanish speakers outside Spain and Mexico. Sending them to the US and Mexico pages is a choice for this example; pick whichever version serves those visitors best.

The same set in an XML sitemap

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://www.example.com/en-gb/running-shoes/</loc>
    <xhtml:link rel="alternate" hreflang="en-US" href="https://www.example.com/en-us/running-shoes/"/>
    <xhtml:link rel="alternate" hreflang="en-GB" href="https://www.example.com/en-gb/running-shoes/"/>
    <xhtml:link rel="alternate" hreflang="en" href="https://www.example.com/en-us/running-shoes/"/>
    <xhtml:link rel="alternate" hreflang="es-ES" href="https://www.example.com/es-es/zapatillas-running/"/>
    <xhtml:link rel="alternate" hreflang="es-MX" href="https://www.example.com/es-mx/tenis-para-correr/"/>
    <xhtml:link rel="alternate" hreflang="es" href="https://www.example.com/es-mx/tenis-para-correr/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://www.example.com/en-us/running-shoes/"/>
  </url>
  <!-- repeat a <url> entry for each version, each with the same child list -->
</urlset>

For PDFs and other files without a head, the same information goes in an HTTP Link header, listing every version on one line:

Link: <https://www.example.com/en-us/size-chart.pdf>; rel="alternate"; hreflang="en-US", <https://www.example.com/en-gb/size-chart.pdf>; rel="alternate"; hreflang="en-GB"

Codes that look right and aren't

WrittenProblemUse instead
en-UKUK is a reserved code, not the United Kingdom's ISO code, so Google ignores the region parten-GB
en-EUEU isn't a country code, so Google ignores that parten, or one tag per country
es-LALA is the code for Laos, so this targets Spanish speakers in LaosOne tag per country (es-AR, es-CO), or es
es-419419 is a UN M.49 region number, not an ISO 3166-1 Alpha 2 codeOne tag per country, or es
mx-esReversed, the language comes firstes-MX
usA country on its own isn't valid, and Google won't derive a language from iten-US
jpA country code, not a language codeja or ja-JP
seThe language code for Northern Sami, not Swedishsv or sv-SE
dkA country code; Danish is dada or da-DK

Latin America is a common trap, because neither LA nor 419 works. You either list each country you serve or use a language-only tag.

Canonicals and hreflang together

Same-language regional pages can be grouped as duplicates, since Google's canonicalization overview counts language versions as duplicates when the primary content is in the same language. Google's international guidance answers that with both tools together: canonical tags plus hreflang, so the correct regional URL is the one shown. In its guide to specifying a canonical URL, Google asks for a canonical in the same language when you use hreflang, and says it prefers URLs that are part of hreflang clusters when choosing canonicals.

In markup, the difference between right and wrong is one line:

<!-- On the en-GB page: correct -->
<link rel="canonical" href="https://www.example.com/en-gb/running-shoes/">
<link rel="alternate" hreflang="en-GB" href="https://www.example.com/en-gb/running-shoes/">
<link rel="alternate" hreflang="en-US" href="https://www.example.com/en-us/running-shoes/">

<!-- On the en-GB page: wrong -->
<link rel="canonical" href="https://www.example.com/en-us/running-shoes/">
<link rel="alternate" hreflang="en-GB" href="https://www.example.com/en-gb/running-shoes/">
<link rel="alternate" hreflang="en-US" href="https://www.example.com/en-us/running-shoes/">

The wrong version tells Google the UK page is a copy of the US page, while the hreflang on the same page presents it as the UK alternate. Make the regional differences visible too: prices in the local currency, local delivery and returns, local spelling. Pages that differ only in a flag icon give Google little reason to treat them as separate. The wider rules for picking canonicals are in the post on how Google treats canonical tags.

Hreflang targets should also be the HTTPS canonical URLs. Google's canonical guide says not to include HTTP versions in hreflang annotations, and pointing a set at URLs that redirect or carry noindex leaves Google to work out which pages you meant.

Head tags, sitemaps or headers

Google's localized versions guide says the three methods are equivalent, and that using all of them at once brings no benefit in Search while making maintenance harder. Choose by page type and by how your site is built:

  • Head tags are the easiest to inspect, but every page carries one line per version. With 30 locales, that is 31 lines in every head once x-default is included. The tags must sit inside a well-formed head, and if a JavaScript framework injects them, check the rendered HTML, as the guide to JavaScript SEO explains.

  • Sitemaps keep the markup out of pages and centralize the logic, and Google says the child xhtml:link entries don't count toward the sitemap URL limit. Errors stay hidden longer, because nobody looks at sitemaps by accident. The post on XML sitemaps that help covers keeping them accurate.

  • HTTP headers are the only option for PDFs and other non-HTML files.

Whatever you choose, don't let two methods disagree for the same page. Two sources that contradict each other are harder to debug than either one alone.

A worked example, a US store adds a UK store

Say a US retailer sells at www.example.com with no locale in its URLs, and it starts shipping to the UK with prices in pounds. Moving the US pages under /en-us/ would turn a launch into a migration, so the US pages stay where they are and the UK store goes under /en-gb/.

ElementBeforeAfter
US product URL/products/trail-shoeUnchanged
UK product URLNone/en-gb/products/trail-shoe
CanonicalsUS page to itselfEach page to itself
HreflangNoneen-US and en to the US page, en-GB to the UK page, x-default to the US page
SitemapsUS URLsUS and UK URLs, each entry listing both versions if hreflang lives there
Prices and deliveryUSD, US shippingGBP with VAT, UK delivery and returns on UK pages
Internal linksUS pagesUK pages link to UK pages; a selector links to the equivalent page, not the home page
UK visitors on US pagesNothingA banner offering the UK store, no forced redirect

Pages that exist only for the US, such as a US-only promotion, get no en-GB annotation. They simply stay out of the set.

Keeping hreflang correct after launch

Hreflang drifts. Templates change in one locale and not another, a product is withdrawn in Germany while the French page still lists it, and two CMS instances generate their sets independently. With the International Targeting report gone, Search Console won't flag any of it, so validation has to come from your own crawls and checks.

  • Generate every set from one source of truth: a table that maps each page to its URL in each locale. Templates and sitemaps read from it, so removing a page in one market updates every set at once.

  • Crawl with a tool that checks return links, code validity and the status of every target, and run it after each release that touches templates or locales.

  • Add automated checks for the basics: every target returns 200, carries a self-referencing canonical, is indexable, and lists the same set as its siblings.

  • Check server logs to see whether Googlebot requests every locale, or keeps crawling one version and neglecting the rest. The log file analysis primer shows how to split crawl activity by folder.

To start, take a handful of URLs from your most important template in each locale and check three things for each one: the set lists the page itself, every target returns 200 with a self-referencing canonical, and every target links back.