How to Do SEO for a Multi-Language Site Without Hreflang Disasters
On this page
- The root mechanism: reciprocity is the whole game
- The failure modes a debugger has to recognize
- The deeper root cause: template fragmentation
- The debugging workflow
- Partial translation: only declare what exists
- What hreflang does and does not do
- Language-only versus language-plus-region
- Sources
- Related posts:
When a German page outranks your French page in France, or Google serves the wrong-language version in search results, the cause is almost never Google ignoring hreflang on principle. It is an implementation defect, and the same one dominates: missing reciprocal return tags. Hreflang is a reciprocal contract. If page A declares page B as its French alternate, page B must declare page A in return, every version in a cluster must list itself, and the cluster needs an x-default. When any of that breaks, Google treats the annotations as untrustworthy and may discard the whole set, falling back to its own guess about which version to rank. This post is about recognizing those failure modes and debugging them, not about setting hreflang up from scratch.
The reason this is a debugging problem rather than a tutorial problem is that hreflang rarely fails at the moment of writing one tag. It fails at scale, weeks later, when separate teams maintaining separate country templates drift out of sync and the reciprocity quietly collapses. Diagnosing that requires a workflow, not a checklist.
The root mechanism: reciprocity is the whole game
Hreflang exists to tell Google which language or regional version of a page to show to which user. It does not raise or lower ranking position. It does not consolidate ranking signals the way a canonical does. It is a targeting instruction, and Google will only honor it if the instruction is internally consistent across the entire cluster.
Consistency means three things hold simultaneously. Every version references every other version, including itself with a self-referential tag. Every reference is mutual, so if A points to B, B points back to A. And the set includes an x-default entry naming the fallback for users whose language and region match none of your specific versions. Break reciprocity on a single page and Google can ignore the annotations for the entire group, not just that one URL. That all-or-nothing behavior is why a small inconsistency produces a site-wide symptom, and why “the tags look fine on this page” is never sufficient evidence.
The failure modes a debugger has to recognize
When you inspect a misbehaving cluster, the symptoms cluster into a handful of recurring errors. Name them so you can recognize them on sight:
- Missing return tags. The single most common failure. A points to B, B does not point back. Google drops the pairing.
- Missing self-reference. A version omits the tag pointing to itself, leaving the cluster incomplete.
- Missing or malformed x-default. No fallback declared, so users outside your targeted markets get an arbitrary version.
- Wrong ISO codes. Language must be ISO 639-1 (lowercase:
en,de,fr,es) and region, when present, ISO 3166-1 Alpha-2 (uppercase:US,GB,DE). Invented combinations likeen-UK(the correct region code isGB) silently invalidate the entry. - Canonical conflict. An hreflang alternate that points at a URL whose canonical names a different page sends Google contradictory instructions; the canonical usually wins and the hreflang is discarded.
- Pointing at non-200 URLs. An alternate that 404s or redirects breaks the reciprocal chain at that link.
Recognize these as the symptoms your debug pass surfaces. The exhaustive catalog of each specific code correction, the clean enumerated reference of every fix, belongs to the dedicated hreflang implementation guide; treat that as the reference you reach for once diagnosis points at a particular error, rather than re-deriving every correction here.
The deeper root cause: template fragmentation
Most large-scale hreflang disasters are not really tag bugs. They are organizational. When the German site is maintained by one team on one template, the French site by another team on another, and the Japanese site by a third, the hreflang block on each becomes a list that every team has to keep in sync with every other team manually. It drifts. A team adds a market and forgets to update the other templates’ return tags. A team changes a URL structure and the alternates elsewhere now point at redirects.
The structural fix is centralization. Generate hreflang from a single source of truth rather than hand-maintaining it per template. Two clean approaches: emit the annotations from one shared CMS component that every locale renders identically, or move hreflang out of the page <head> entirely and into an XML sitemap that lists each URL with all its alternates in one place. The sitemap approach is often the more maintainable at scale precisely because it removes the per-template duplication that causes drift. Either way, the goal is that no human is manually reconciling reciprocity across teams.
The debugging workflow
Diagnose in this order rather than guessing:
- Inspect one page’s tags. Pull the actual rendered hreflang annotations for a single affected URL (view source or fetch the served HTML, or read the sitemap entry). Confirm it lists itself, every alternate, and x-default with valid codes.
- Verify each alternate points back. Walk to each declared alternate and confirm it references the page you started from. This is where missing return tags reveal themselves. A one-page inspection cannot catch reciprocity failures; you have to traverse the cluster.
- Bulk-crawl validate. Run a crawler that parses hreflang across the whole site and reports non-reciprocal pairs, missing self-references, bad codes, and alternates resolving to non-200 status. This converts a per-page chore into a site-wide report. Note that Google Search Console no longer offers a site-wide hreflang validation surface: the International Targeting report, which once flagged these errors, was retired in September 2022, so third-party crawlers and hreflang validators are now the practical tooling for bulk checks.
- Standardize the URLs. Pick a canonical URL form (consistent www/non-www, trailing slash, protocol) and make every hreflang reference use it exactly. Trivial-looking URL inconsistencies break matching.
- Monitor over time. Reciprocity rots as the site changes. Re-run the bulk validation on a cadence, not once.
Partial translation: only declare what exists
A frequent self-inflicted wound is declaring hreflang for versions that are not actually fully there. If a page exists in English and German but the French equivalent is missing or only partially translated, do not list a French alternate that resolves to a fallback, a thin stub, or a 404. Scope hreflang strictly to the versions that genuinely exist and serve real translated content. Declaring phantom alternates breaks the reciprocal chain and tells Google to consider versions that cannot satisfy the user.
What hreflang does and does not do
Hold the boundary clearly, because misunderstanding it sends people debugging the wrong thing. Hreflang selects which existing version Google shows in which market. It does not make any version rank higher, it does not merge ranking signals across versions, and it does not fix a page that simply is not competitive in its target market. If your French page does not rank in France even with perfect reciprocal annotations, the problem is the page’s relevance and authority in that market, not the hreflang. Diagnose those separately. Hreflang only ever decides, among versions Google has already chosen to rank, which one a given user sees.
Language-only versus language-plus-region
A recurring source of confusion is choosing between en and en-US. The rule that resolves most of it: target language alone unless the regional distinction is real. A single English page meant for every English speaker should use en, not en-US, because en-US narrows the targeting to one country and can make Google skip the page for English searchers elsewhere. Use a region suffix only when you genuinely maintain separate versions for separate markets (different currency, pricing, shipping, or legal terms for en-US versus en-GB versus en-AU), and even then the region code modifies the language; there is no region-only annotation. Declaring en-US and en-GB for two pages whose content is identical gives Google two equivalent options and no real reason to prefer one per market, which wastes the annotation and can produce the wrong-version-served symptom you were trying to prevent.
Where it surfaces in debugging is mismatched granularity across a cluster. If one template emits fr while another emits fr-FR for the same content, the references no longer line up and reciprocity breaks at the seam. Pick one granularity per language, apply it everywhere, and verify the codes match exactly across the set, since fr and fr-FR are different keys to the matching logic even though they read as the same language to a human.
Sources
Google Search Central, Tell Google about localized versions of your page: https://developers.google.com/search/docs/specialty/international/localized-versions
Google Search Console Help, The International Targeting report is deprecated: https://support.google.com/webmasters/answer/12474899