By SitemapFixer Team
Updated April 2026

Hreflang Conflicts: Diagnose and Fix at Scale

Audit hreflang conflicts freeAudit hreflang free

Hreflang is unforgiving. A single missing return tag or a typo like en-uk instead of en-gb is enough to make Google ignore the entire cluster — and because Google fails silently, you only notice when the wrong language version starts ranking in the wrong country. This guide focuses on the conflicts that crawlers like Screaming Frog, Sitebulb, and Merkle's validator surface, why they happen, and how to fix them at scale across hundreds or thousands of URLs. For per-error walkthroughs, see the hreflang errors guide.

What Counts as a Hreflang Conflict

Not every hreflang warning is a true conflict. A "conflict" is when two signals about the same URL contradict each other in a way that forces Google to discard the cluster. The four conflict patterns that matter:

Return-tag mismatch. Page A declares Page B as its fr-fr alternate. Page B does not declare Page A back, or declares a different URL as its en-us alternate. Hreflang requires bidirectional confirmation — without the return tag, Google treats the relationship as unverified and ignores it.

Canonical pointing to a non-target language. The French page (/fr/produit) declares hreflang alternates correctly, but its canonical points to the English version (/en/product). Google follows the canonical, drops the French URL from its index, and the entire hreflang cluster collapses because there is no canonical French URL to map alternates onto.

Conflicting declarations across HTML, sitemap, and HTTP headers. Hreflang can be declared in three places. If the HTML <link> says one set of alternates and the sitemap <xhtml:link> says a different set, Google sees two competing definitions and applies neither consistently.

Language-region code mismatch. A page declared as en-uk (which is invalid — the ISO code is gb, not uk) silently fails. The page may have a perfectly valid English-UK alternate that never gets recognized because the code is wrong.

The Five Most Common Hreflang Errors and How to Spot Them

1. "No return tags" — Screaming Frog flags this as Hreflang: Missing Confirmation Links. The English page lists a French alternate, but the French page does not list the English one back. Common cause: hreflang generated from a CMS field that is only populated on the source language page, not on translations.

2. "Inconsistent hreflang URLs" — Page A claims Page B as fr-fr; Page B's return tag points back to Page C, not Page A. Usually a redirect chain or a stale URL in a translation memory tool.

3. "Hreflang to non-200 URL" — One of the alternates returns 301, 302, or 404. Hreflang must point to canonical, indexable, 200-status URLs. Redirected alternates are treated as missing.

4. "Canonical conflict with hreflang" — The page's self-canonical does not match its self-referencing hreflang entry, or the canonical points to a different language version. Sitebulb specifically flags this as Hreflang Canonical Confusion.

5. "Invalid language code"en-uk, en-eu, cn-zh, jp, br-pt. Language codes follow ISO 639-1; region codes follow ISO 3166-1 Alpha-2. The order is always language-REGION. uk is Ukrainian, not the United Kingdom; eu is not a valid region; Chinese is zh, not cn; Japanese is ja, not jp; Brazilian Portuguese is pt-br, not br-pt.

To spot all five at once, run a Screaming Frog crawl with the Hreflang configuration enabled (Configuration → Spider → Crawl → tick "hreflang"), then export the Reports → Hreflang → All table. Sort by issue type. The same patterns aggregate to a small number of root causes — usually a single broken template or translation field.

Correct Return-Tag Setup

Every URL in a hreflang cluster must declare every other URL — including itself. For a three-language site (English US, French France, German Germany), each of the three pages must contain the same four <link> tags. Anything less is a return-tag conflict.

<!-- On https://example.com/en-us/product -->
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="de-de" href="https://example.com/de-de/produkt" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en-us/product" />

<!-- On https://example.com/fr-fr/produit -->
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="de-de" href="https://example.com/de-de/produkt" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en-us/product" />

<!-- On https://example.com/de-de/produkt — same four tags, identical -->

The principle: every page in the cluster ships an identical block of hreflang tags. If you find yourself generating different blocks per page, your template logic is the source of the conflict. Generate the cluster once per route group and inject the same block on every language variant.

Mismatched Return Tag — A Concrete Wrong Example

Here is what a return-tag conflict actually looks like in HTML. The English page lists three alternates; the French page only lists two of them and points its English alternate at a slightly different URL (a redirect target).

<!-- WRONG: On https://example.com/en-us/product -->
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="de-de" href="https://example.com/de-de/produkt" />

<!-- WRONG: On https://example.com/fr-fr/produit -->
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="en-us" href="https://example.com/product" />
<!-- ^ points to a URL that 301s to /en-us/product
     ^ no de-de tag at all
     ^ no self-referencing hreflang on some implementations -->

Both errors here are common in the wild. The redirect-chained alternate happens when teams change URL structures (e.g., adding the locale prefix) without updating translation memory. The missing de-de happens when the German page is added later and the French template is not regenerated.

Conflicting Canonical and Hreflang

The hreflang–canonical interaction is where most international sites bleed indexation. The rule: every hreflang alternate must be canonical-to-itself. If the French page's canonical points anywhere other than the French URL, the entire cluster is invalidated.

<!-- WRONG: French page canonicalises to English -->
<!-- On https://example.com/fr-fr/produit -->
<link rel="canonical" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<!-- Google: "fr-fr/produit isn't canonical, drop it from the cluster" -->

<!-- CORRECT: every page is canonical to itself -->
<!-- On https://example.com/fr-fr/produit -->
<link rel="canonical" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />

This bug appears most often on e-commerce sites where translation plugins generate separate URLs but the underlying product template still emits a canonical pointing to the "master" English product. For deeper coverage of this exact pattern see the hreflang and canonical guide.

Why Mixing HTML and Sitemap Hreflang Creates Conflicts

Google supports three hreflang declaration methods: HTML <link> tags, HTTP Link headers, and sitemap <xhtml:link> annotations. You should pick one. Combining methods is technically permitted, but it almost always produces conflicts because the methods drift over time.

A common pattern: a developer adds hreflang to the HTML head when shipping the multilingual feature. Months later, an SEO consultant adds hreflang to the sitemap as a "belt and braces" measure, copying URLs from a spreadsheet that is missing two locales. Now every URL in the cluster has two competing hreflang definitions, and Google must reconcile them. When the sitemap and HTML disagree on which URL is the fr-fr alternate, Google does not pick a winner — it discards the cluster.

<!-- sitemap.xml — declares fr-fr alternate as /fr/produit -->
<url>
  <loc>https://example.com/en-us/product</loc>
  <xhtml:link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product"/>
  <xhtml:link rel="alternate" hreflang="fr-fr" href="https://example.com/fr/produit"/>
  <xhtml:link rel="alternate" hreflang="de-de" href="https://example.com/de/produkt"/>
</url>

<!-- HTML on https://example.com/en-us/product — declares fr-fr alternate as /fr-fr/produit -->
<link rel="alternate" hreflang="en-us" href="https://example.com/en-us/product" />
<link rel="alternate" hreflang="fr-fr" href="https://example.com/fr-fr/produit" />
<link rel="alternate" hreflang="de-de" href="https://example.com/de-de/produkt" />

<!-- Conflict: sitemap says /fr/produit, HTML says /fr-fr/produit. Google discards both. -->

Pick one method and remove the other. For most sites we recommend HTML for small clusters (under ~50 alternates per page) and sitemap for large catalogs where the HTML head would balloon — see the hreflang sitemap guide for the implementation tradeoffs.

Language Code Typos That Create Silent Conflicts

Hreflang silently ignores invalid codes. The page parses, the crawler may not even flag a warning depending on its strictness, but Google does not associate the URL with its intended locale. The most damaging typos:

en-uk instead of en-gbuk is the ISO code for Ukrainian. en-uk means "English as spoken in Ukraine" which is not a valid combination Google recognises for the United Kingdom. Use en-gb.

en-eu — there is no "EU" region code. If you want a fallback for European English readers, use en (language only) or x-default.

cn-zh or zh-cn reversal — language must come first, region second. Chinese in mainland China is zh-cn; Chinese in Taiwan is zh-tw; Hong Kong Chinese is zh-hk. Never cn-zh.

br-pt instead of pt-br — Portuguese in Brazil is pt-br. Putting the country first is one of the most common errors on Latin American sites.

jp instead of jajp is the country code for Japan, not the language. Japanese is ja. If you want to target Japanese speakers in Japan specifically, use ja-jp.

To catch these in bulk, run this regex over your sitemap or HTML output to find any code that does not match the expected pattern:

# Extract all hreflang codes from sitemap and validate against ISO list
curl -s https://example.com/sitemap.xml | \
  grep -oE 'hreflang="[^"]+"' | \
  sort -u

# Expected output format: lowercase language, optional dash + lowercase region
# en, en-us, en-gb, fr-fr, de-de, ja-jp, zh-cn, x-default
# RED FLAGS: en-uk, en-eu, cn-zh, jp, br-pt, EN-US (wrong case)

Debugging Conflicts with GSC URL Inspection

Google Search Console removed the dedicated International Targeting report in 2022, but the URL Inspection tool exposes everything you need. Paste a URL from a suspect cluster, expand "Coverage" and "HTTP response", and you will see:

User-declared canonical vs Google-selected canonical. If these differ, your hreflang cluster is broken at the canonical layer — fix that first.

Referring page and Sitemaps. Confirms which sitemap Google associates with the URL, helping you spot when a URL is in two sitemaps with different hreflang annotations.

Live Test → View Crawled Page → More Info → HTTP Response. Shows the exact HTML Google parsed, including the rendered hreflang block. If you see fewer <link rel="alternate"> tags here than in the source, your JavaScript is injecting hreflang client-side and Googlebot may not be picking them up.

Run URL Inspection on at least one page from each language variant. The conflict often only shows up on one side of the cluster — the English page looks clean, the French page reveals a canonical mismatch.

Auditing and Fixing Conflicts at Scale

For sites with more than a few hundred URLs per locale, manual fixes are not feasible. Use a two-step pipeline: audit script to identify the conflict pattern, then a mass-update of the sitemap or template.

#!/bin/bash
# Step 1: Pull all hreflang declarations from the sitemap
curl -s https://example.com/sitemap.xml > sitemap.xml

# Step 2: For each <loc>, fetch the page and extract HTML hreflang
xmllint --xpath '//loc/text()' sitemap.xml | while read URL; do
  echo "=== $URL ==="
  curl -s "$URL" | grep -oE '<link[^>]*hreflang="[^"]+"[^>]*>'
done > html-hreflang.txt

# Step 3: Diff sitemap-declared vs HTML-declared alternates
# (both must list the same set of URLs for each language)
# Conflicts are pages where the two sets differ.

# Step 4: Validate every alternate returns 200 (no redirects, no 404s)
grep -oE 'href="[^"]+"' html-hreflang.txt | \
  sed 's/href="//;s/"//' | sort -u | while read ALT; do
  CODE=$(curl -o /dev/null -s -w "%{http_code}" "$ALT")
  [ "$CODE" != "200" ] && echo "BAD $CODE: $ALT"
done

Once you know the pattern, the mass fix is almost always one of: (a) regenerate the sitemap from the canonical URL list with a script that emits identical hreflang blocks per cluster; (b) fix the template that emits HTML hreflang so it pulls from the same source as the sitemap; or (c) remove the redundant declaration method entirely. SitemapFixer can run the audit and emit a corrected sitemap with consistent hreflang blocks across all clusters in a single pass.

Tools That Detect Hreflang Conflicts

Screaming Frog SEO Spider. Configuration → Spider → Crawl → enable hreflang. After the crawl, open Reports → Hreflang. The most useful sub-reports: Missing Confirmation Links (return-tag mismatches), Inconsistent Language Confirmation Links, Non-200 Hreflang URLs, and Missing X-Default. Free up to 500 URLs; paid licence required for larger sites.

Sitebulb. The Hreflang report is more visual than Screaming Frog and shows cluster diagrams that make return-tag gaps obvious. Particularly strong at flagging hreflang–canonical conflicts as a dedicated category.

Merkle Hreflang Tags Testing Tool. Free, web-based, single-URL. Paste a URL and it returns the cluster Google would see, with each return tag validated. Best for spot-checking specific URLs after a fix; not for site-wide audits.

SitemapFixer. Pulls every URL from your sitemap, validates hreflang clusters bidirectionally, flags canonical conflicts, and outputs a corrected sitemap. Built for the bulk-fix step, not just discovery.

Google Search Console URL Inspection. Authoritative — it shows exactly what Google has parsed. Slow for bulk work but the final word on whether a fix has actually registered. See rel=alternate hreflang fundamentals for a refresher on what GSC expects to see.

Post-Fix Recrawl Timeline

Hreflang re-evaluation is slower than canonical re-evaluation because Google must crawl every URL in the cluster before it can verify the return tags. Realistic timeline expectations:

Days 1–3: Submit the updated sitemap (with hreflang annotations if you use the sitemap method) via GSC → Sitemaps → Resubmit. Use URL Inspection → Request Indexing on 5–10 representative URLs across language variants. High-traffic pages get re-crawled within 24–72 hours.

Days 3–14: Google re-crawls the bulk of your high and medium priority URLs. Re-run URL Inspection on a sample — the "User-declared canonical" and "Google-selected canonical" should now match, and the rendered HTML should show the corrected hreflang block.

Weeks 2–4: Long-tail URLs get re-crawled. Hreflang clusters that were previously invalidated start being honoured again. You may notice ranking improvements in target locales as Google starts serving the correct language version per region.

Weeks 4–8: Full propagation. If the conflict is still flagged in Screaming Frog or Sitebulb at this point, the fix did not deploy correctly — re-audit. The most common reason fixes do not propagate is CDN cache: purge Cloudflare/Fastly/Vercel caches immediately after the deploy or Googlebot will see stale HTML for days.

One reliable signal that the fix landed: GSC URL Inspection on the previously-conflicted URLs should now show consistent canonical and return-tag information, and the "Hreflang" section in Live Test should list every alternate URL with no warnings.

Preventing Conflicts From Recurring

Most international sites fix hreflang once and accumulate conflicts again over 6–12 months as new pages, locales, and CMS plugins drift out of sync. Prevention requires three habits: a single source of truth for hreflang clusters (one script, one config file, one CMS field — never multiple); a CI check that fails the build if any sample URL returns hreflang tags that do not bidirectionally validate; and a monthly automated crawl that compares the cluster set month-over-month. Pair this with an x-default fallback on every cluster so visitors from unmapped regions still resolve to a sensible page.

Related Guides

Find every hreflang conflict on your site
Free analysis in 60 seconds
Audit My Site Free
Related guides