International & hreflang Migrations

Context

A multi-language estate multiplies every migration problem by the number of locales, and adds one that monolingual sites never meet: the pages are not independent. hreflang annotations bind a set of URLs into a group that search engines treat as alternates of one another, and the binding only holds if every member points at every other member and each of them points back. Change the URLs on one side of that relationship and the whole set stops being reciprocal — at which point search engines fall back to treating the pages as unrelated duplicates and start choosing between them on their own. This work runs alongside URL Mapping & Redirect Architecture and cannot be done after the redirects, because the annotations have to change in the same deploy as the URLs they reference.

The second complication is that international sites usually carry geographic redirection, and geographic redirection composes badly with migration redirects. A rule that sends European visitors to /de/ and a rule that maps a legacy path to a new one can, between them, produce a loop that neither rule contains — and because the loop depends on the visitor’s inferred location, it reproduces for some people and not for the team testing it.

The third is that the migration frequently is the structure change: moving from country-code domains to subfolders, or the reverse, is one of the most common reasons an international estate migrates at all. That means the URL scheme, the annotations, and the geographic logic all move together, which is precisely the combination that makes staged verification difficult and makes a complete pre-migration inventory of the locale graph essential.

What breaks an hreflang set during a migration Five conditions that invalidate an alternate set — a missing self-reference, a one-way link, an alternate that redirects, a duplicate x-default and an invalid region code — with how each is detected. A set is valid only if every member agrees with every other Condition Effect Detected by Missing self-reference set ignored entirely grep the page for its own URL One-way annotation that member drops out reciprocity check per pair Alternate returns 3xx member not counted status sweep of every alternate Two x-default entries both discarded count per set Invalid region code that entry ignored validate against the code list None of these produce an error anywhere — the annotations are simply disregarded, and the pages compete with each other instead.
Every check is cheap and mechanical, which is why they belong in the generator rather than in a review.

Pre-flight Checks

Capture the current locale graph and its behaviour before anything moves — none of it can be reconstructed once the estate has changed shape.

  • Export every URL with its hreflang annotations, so the existing alternate sets are recorded as data rather than inferred later.
  • Confirm reciprocity on the current estate — every alternate must list every other, including itself — and record which sets already fail.
  • Note where the annotations live: <head> markup, HTTP headers, or the XML sitemaps. All three are valid and a site can use more than one.
  • Record the current x-default target for each set, and whether one exists at all.
  • Document the geographic redirection logic, including what it keys on and whether it fires on every request or only on entry.
  • Check that every locale URL returns 200 and is self-canonical; an alternate that redirects or canonicalises elsewhere is ignored.

International Readiness Checklist:

Execution Steps

Each step below consumes the artefact produced by the one before it, and the last two must reach production in the same release.

1. Export the Locale Graph as Data

This export is the artefact everything downstream is generated from, so capture it before any structural decision is finalised rather than after. Build a table of every page, its locale, and its alternates before touching anything. This is the artefact the whole migration depends on, because the new annotations have to be generated from the mapping of old URLs to new ones and there is no way to derive them afterwards from a site that has already changed. Take it from the crawl produced by Crawl Baseline Generation rather than from the CMS, since the CMS records intent and the crawl records what is actually published.

Export from the rendered pages rather than from the content system. A CMS knows which translations it believes exist; the published estate knows which ones actually emit annotations, and the two diverge more than anybody expects — translations marked complete but never published, locales enabled in configuration and never populated, pages whose annotations were hand-edited years ago and never revisited. The graph you migrate has to be the published one.

Record reciprocity status as part of the export, not as a later check. A meaningful share of large international estates already have broken sets before any migration starts, and knowing which ones were broken beforehand is what stops you attributing a pre-existing fault to your own change — and what stops you spending the window fixing something that was never working.

2. Map Every Locale, Not Just the Primary One

The most common international migration failure is that the primary language is mapped carefully and the secondary locales are handled by a pattern that almost works. Locale URL structures diverge in practice — a translated slug here, a locale that never had a given page, a market where a section was retired years ago — so the mapping needs the same per-URL treatment described in CSV Mapping Workflows, applied per locale rather than once.

Expect the locales to disagree with each other structurally. A pattern that transforms /en/products/widget into /products/widget will do something plausible and wrong to /de/produkte/widget, because the translated path segment is not where the pattern expects it. Treat each locale as its own mapping exercise sharing a common approach, and reconcile the results into one table keyed by the set rather than by the URL.

Watch for locales that legitimately lack a page. Not every market publishes every article, and a set with three members where another has five is normal rather than a defect — but a generated mapping that assumes symmetry will invent URLs for the missing ones, and an annotation pointing at a page that does not exist invalidates the set just as thoroughly as a broken one.

3. Regenerate Annotations From the New Mapping

Emit the new hreflang sets from the mapping table, not by editing the existing ones. Every member of a set must reference the post-migration URL of every other member including itself, and doing that by hand across dozens of locales guarantees an asymmetry somewhere. The mechanics of reciprocity and the failure modes are covered in Updating hreflang Sets After a Domain Change.

Keep the delivery mechanism consistent across the estate. A site that annotates most pages in markup and a handful in the sitemap is valid but far harder to verify, because a reciprocity check has to consult two sources and reconcile them — and the pages that end up in the minority mechanism are invariably the ones nobody remembers when the graph is regenerated.

4. Deploy Annotations and Redirects Together

The annotations and the URLs they describe must change in the same release. Ship the redirects first and the annotations point at URLs that now redirect; ship the annotations first and they point at URLs that do not yet exist. Either ordering produces a window in which the whole set is invalid, and search engines do not wait politely for the second half.

Generate every member of the set in one operation so the output is symmetric by construction. The property you need — each member lists all members including itself — is trivially satisfied by emitting from a single mapping table, and almost impossible to maintain by editing pages individually. Where the annotations live in the sitemap rather than in markup, the same principle applies to the sitemap generator.

Validate the generated output before deploying it. A count of members per set, a check that each set contains exactly one x-default, and a confirmation that every referenced URL appears in the new-URL column of the mapping will catch the great majority of generation faults in a few seconds, and all three are simple assertions over the table you have just produced.

5. Verify Geographic Logic Against the New Paths

Geographic redirection written against the old URL structure will not match the new one, and a rule that no longer matches simply stops firing — which is a better failure than one that fires wrongly, but is still a change nobody chose. Re-test from several regions and confirm both that the intended redirect happens and that it terminates, following the approach in Preventing Geo-Redirect Loops During Migration.

Comparing international URL structures Country-code domains, subdomains and subfolders compared on migration cost, geographic signalling, and how authority is shared across locales. The structure decides how much authority each locale inherits Structure Example Authority Migration cost ccTLD example.de separate per market highest — separate estates Subdomain de.example.com partly shared moderate Subfolder example.com/de/ fully shared lowest Parameter ?lang=de shared but ambiguous low, and not recommended Consolidating ccTLDs into subfolders is the most common international migration precisely because of the authority column.
The bottom row is included for completeness — parameters are a poor signal and should be migrated away from rather than to.

Plan the deploy as a single atomic release even where the two halves are owned by different teams. In practice that usually means the annotations are generated as part of the same build that produces the redirect configuration, reviewed in the same change, and shipped by the same pipeline — which is easier to arrange before the window than to negotiate during it.

Configs / Commands

The fragments below cover the three delivery mechanisms and the check that matters most. Use one mechanism consistently rather than mixing them per locale, because a set annotated partly in markup and partly in headers is far harder to verify and no easier to maintain.

Markup annotations — every member lists every member, including itself:

<!-- On /en/pricing/ — the self-reference is required, not optional -->
<link rel="alternate" hreflang="en" href="https://example.com/en/pricing/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/preise/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/tarifs/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/pricing/" />

HTTP header annotations — for PDFs and other non-HTML resources:

# Link headers carry the same semantics where there is no <head> to put them in
add_header Link '<https://example.com/en/guide.pdf>; rel="alternate"; hreflang="en",
  <https://example.com/de/anleitung.pdf>; rel="alternate"; hreflang="de"' always;

Verify reciprocity across a set with curl:

# For each alternate, confirm it lists the page we started from
for u in /en/pricing/ /de/preise/ /fr/tarifs/; do
  echo "== $u"; curl -s "https://example.com$u" | grep -o 'hreflang="[a-z-]*"' | sort -u
done
Deploying annotations and redirects in one release The locale graph exported, new URLs mapped per locale, annotations regenerated from that mapping, and both shipped in a single deploy so no window exists where the set is invalid. Annotations and redirects ship together, or not at all Export graph current alternates Map per locale not by pattern Regenerate from the mapping Deploy both one release Redirects first, and every annotation points at a URL that now redirects — so no alternate returns 200 and the whole set is discarded. Annotations first, and they point at URLs that do not yet exist. Both orderings invalidate the set for as long as the gap lasts.
This is the one part of an international migration with no safe staged rollout: the two halves describe each other.

Validation

Sample from every locale rather than uniformly, and treat a failure anywhere as a failure of the release rather than of one market. Every check below runs against the deployed estate, because annotations are only meaningful in the responses that carry them.

Sample the verification by locale rather than uniformly. A random sample across a large estate will be dominated by the biggest locale and can pass comfortably while a smaller market is entirely broken, which is the specific failure this section exists to prevent. Take a fixed number of sets from each locale, including the smallest, and treat a failure in any locale as a failure of the release.

Rollback Triggers

Annotation faults degrade slowly rather than breaking loudly, so these thresholds are trend-based.

  • Reciprocity failures on more than 2% of sampled sets after deploy — revert the annotation change and regenerate.
  • Any locale showing a sustained impression decline while others hold, over two consecutive weekly readings.
  • A geo-redirect loop reproducible from any monitored region — revert the geographic rule immediately.
  • Alternate URLs returning 3xx rather than 200 on more than 1% of the set.
  • The wrong locale being served to a region for more than one hour after the switch.

One structural decision worth settling early: whether the migration is also a change of international URL structure. Consolidating country-code domains into subfolders, or splitting subfolders into separate domains, changes the authority model as well as the addresses, and doing it in the same window as a hosting move compounds two substantial risks. Where the schedule allows, separate them — move the infrastructure first and restructure the locales afterwards, or the reverse — so that when something degrades you can tell which change caused it.

FAQ

Does hreflang need to change if only the domain changes and paths stay identical? Yes — every annotation contains an absolute URL, so a domain change invalidates all of them at once even though the path structure is untouched. This is the case teams most often miss, because nothing about the site’s structure has changed and the annotations are easy to forget. Regenerate the whole graph with the new hostname and deploy it in the same release as the redirects.

What happens if reciprocity is broken for a while? Search engines stop treating the pages as alternates and fall back to evaluating them independently, which usually means choosing one and largely ignoring the others. The effect is gradual rather than immediate and recovers once reciprocity is restored, but recovery takes as long as re-crawling the whole set. That asymmetry — fast to break, slow to repair — is why the annotations ship with the redirects rather than as a follow-up task.

Should annotations live in markup, headers, or the sitemap? Whichever one you can generate reliably and verify easily; the semantics are identical. Sitemap annotations scale best for very large estates because they keep the graph in one file rather than duplicated across every page, but they are also the version people forget to regenerate. Markup is the easiest to verify with a request. Pick one, apply it everywhere, and do not mix.

Is x-default required? Not required, and worth having. It names the page to serve when no annotated locale matches the visitor, which without it is a decision the search engine makes for you. Point it at a genuine language-selection page or at your most general locale, make sure it resolves, and ensure exactly one member of each set declares it.

How do geo-redirects interact with the annotations? Badly, if the redirect fires for crawlers. An annotation naming /de/ is useless if a request for that URL is redirected somewhere else, because the alternate must return 200 to count. Geographic redirection is best applied as a suggestion — a banner offering the local version — rather than as an unconditional redirect, and where a redirect is genuinely required it must exempt the annotated URLs themselves.

How long does a broken set take to recover once reciprocity is restored? As long as it takes for every member to be re-crawled, which for a large international estate is weeks rather than days and is not something you can accelerate meaningfully. That asymmetry is the single strongest argument for getting the annotations right in the deploy rather than treating them as a follow-up: breaking a set is instantaneous and repairing one runs at crawl speed. Where a set does break, prioritise re-crawling by submitting the affected URLs in an updated sitemap rather than by requesting indexing page by page.

Should the legacy locale URLs keep redirecting indefinitely? Yes, for the same reasons as any other migrated URL, and with one addition specific to international estates: inbound links to locale pages are often held by partners, directories and press in that market, and are far less likely to be updated than links to your primary language. Treat the legacy locale paths as permanent redirect obligations, and be especially wary of retiring a ccTLD entirely — the domain itself carries market recognition that outlives its usefulness as a serving host.

Related

← Back to URL Mapping & Redirect Architecture

Explore Sub-topics