/ โ† Hreflang Fixes / Sitemap Mismatches
In this guide
  1. Why two sources drift apart
  2. The kinds of mismatch
  3. Comparing the two sources properly
  4. Which source to keep
  5. A valid hreflang sitemap, line by line
  6. Generating the sitemap from one locale map
  7. The fix, step by step
  8. Questions and answers

Why two sources drift apart

Google treats the three declaration methods, HTML link tags, HTTP headers and sitemaps, as equivalent. Using more than one is allowed. The trouble is that they are rarely produced by the same code. A typical WordPress site might have WPML printing link tags and an SEO plugin building the sitemap; a custom site might have hreflang in a template and a nightly job writing the sitemap from the database. Each reads the site at a different time, from a different place, with different rules for building addresses.

When the two agree, the duplication is harmless but pointless. When they disagree, you are sending Google two versions of the same cluster, and there is no documented rule for which one wins. Google may combine them, prefer one, or distrust the relationship. You lose control of which page is shown where.

๐Ÿ’ก Search Console no longer has an International Targeting report, and the Sitemaps report tells you whether a sitemap was read, not whether its hreflang is right. A crawler that compares the two sources, such as the Hreflang Checker, is the practical way to catch a mismatch.

The kinds of mismatch

MismatchExampleUsual cause
Different locale listsHTML has en, fr, de; sitemap has en, frNew language added to the plugin but not to the sitemap job
Different addressesHTML says /de/kontakt/; sitemap says /de/contact/Sitemap builds localised slugs from the source language
Different codesHTML says en-GB; sitemap says enEach tool maps languages to codes its own way
x-default only in oneHTML has x-default; sitemap has nonex-default added later to one source
Stale entriesSitemap still lists a locale that was removedSitemap cached or generated from an old export
Different hosts or slasheswww. in one, no www. in the otherSeparate base URL settings
Sitemap lists non-canonical pagesParameter or paginated addresses in locGenerator reads every URL in the database, not only canonical ones

Comparing the two sources properly

Eyeballing a sitemap next to page source works for one page, not for a site. A short script can pull the cluster for every address from both sources and report the differences. This example reads one sitemap and fetches each page listed in it:

import re, requests, xml.etree.ElementTree as ET

NS = {"s": "http://www.sitemaps.org/schemas/sitemap/0.9",
      "x": "http://www.w3.org/1999/xhtml"}
tree = ET.fromstring(requests.get("https://example.com/sitemap-pages.xml").content)

for url in tree.findall("s:url", NS):
    loc = url.find("s:loc", NS).text
    in_map = {(l.get("hreflang").lower(), l.get("href"))
              for l in url.findall("x:link", NS)}
    page = requests.get(loc).text
    in_html = {(c.lower(), h) for c, h in re.findall(
        r'hreflang="([^"]+)"\s+href="([^"]+)"', page)}
    if in_map != in_html:
        print(loc)
        print("  only in sitemap:", sorted(in_map - in_html))
        print("  only in HTML:   ", sorted(in_html - in_map))

The regular expression assumes hreflang comes before href in each tag; adjust it if your templates write them the other way round. Run it on a sample first, and be gentle with request rates on large sites.

Which source to keep

There is no ranking benefit to either method. Choose the one your team can keep correct with the least effort.

Keep the HTML head ifKeep the sitemap if
The site has a handful of localesThe site has many locales, so head blocks get long
Your CMS or framework already outputs tags correctlyPages are rendered by several systems that are hard to change
Editors and developers want to check a page with View SourceA central data team already builds sitemaps from a product or page database
Pages are added often and the sitemap lagsYou also need hreflang for non-HTML files such as PDFs

If you keep the HTML, the sitemap still lists your pages; it just drops the xhtml:link entries. If you keep the sitemap, remove the link tags from the templates. Either way, the rule is one source of hreflang, built from the same locale list the site uses for its menus and switcher.

โš ๏ธ Do not delete one source until the other is complete and correct. For a short period, having both identical is safer than having one broken.

A valid hreflang sitemap, line by line

If the sitemap is the source you keep, it has to be right in every detail. This example covers a contact page in three languages plus a fallback. Every address in the cluster gets its own url entry, and every entry repeats the full set of alternates, including itself:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <url>
    <loc>https://example.com/contact/</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/contact/"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/kontakt/"/>
    <xhtml:link rel="alternate" hreflang="nl" href="https://example.com/nl/contact/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/contact/"/>
  </url>
  <url>
    <loc>https://example.com/de/kontakt/</loc>
    <xhtml:link rel="alternate" hreflang="en" href="https://example.com/contact/"/>
    <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/kontakt/"/>
    <xhtml:link rel="alternate" hreflang="nl" href="https://example.com/nl/contact/"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/contact/"/>
  </url>
  <!-- and a third url entry for /nl/contact/ with the same four links -->
</urlset>

Alternate pages can sit in different sitemap files, and even on different hosts if you have verified ownership of each in Search Console, but keeping each cluster together in one file makes it far easier to check.

Generating the sitemap from one locale map

The durable fix is to generate hreflang from the same data the site uses. Here is a minimal generator that takes a mapping of page keys to their address in each locale:

from xml.sax.saxutils import escape

PAGES = {
    "contact": {"en": "https://example.com/contact/",
                "de": "https://example.com/de/kontakt/",
                "nl": "https://example.com/nl/contact/"},
}
DEFAULT = "en"

out = ['<?xml version="1.0" encoding="UTF-8"?>',
       '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
       'xmlns:xhtml="http://www.w3.org/1999/xhtml">']
for versions in PAGES.values():
    links = [(code, url) for code, url in sorted(versions.items())]
    links.append(("x-default", versions[DEFAULT]))
    for url in versions.values():
        out.append("  <url><loc>%s</loc>" % escape(url))
        for code, href in links:
            out.append('    <xhtml:link rel="alternate" hreflang="%s" href="%s"/>'
                       % (code, escape(href)))
        out.append("  </url>")
out.append("</urlset>")
open("sitemap-pages.xml", "w", encoding="utf-8").write("\n".join(out))

In a real site, PAGES comes from your CMS, database or routing configuration, and pages whose translation is not published are simply absent from their locale's entry. The same mapping should feed the language switcher, so a page cannot appear in one and not the other.

For general sitemap problems unrelated to hreflang, such as entries that return errors, see the guide to sitemap mismatches and oversized sitemaps.

The fix, step by step

Step 1

Extract hreflang from both sources

Run the Hreflang Checker to see which clusters it flags for sitemap inconsistency, then gather the full picture: read every sitemap listed in robots.txt or your sitemap index, extract each loc with its xhtml:link entries, and fetch the same pages to extract their head link tags. Normalise language codes to lower case for comparison, but keep addresses exactly as written, because a slash or host difference is part of the problem.

Step 2

Classify each difference

Sort every mismatch into a type: locale present in only one source, different address for the same locale, different code for the same page, x-default in only one source, stale entries for removed pages, or non-canonical addresses in the sitemap. Count each type. Most sites find that one or two types account for nearly all differences, and each type traces back to a specific setting, plugin or generation job that you can fix once.

Step 3

Choose the source you will maintain

Decide between the HTML head and the sitemap based on how many locales you have, which system already produces correct output, and who will maintain it. Pick the one that reads from the same locale and address data as your language switcher. Record the decision where developers will see it, such as the repository readme or the plugin settings notes, so that a future tool does not quietly add the other method back.

Step 4

Remove or regenerate the other source

First make the chosen source complete and correct for every cluster. Then remove hreflang from the other: switch off the sitemap generator's alternate links, or remove link tags from the templates or translation plugin. If you keep the sitemap, regenerate it from your locale map so it contains only canonical, indexable pages, with every alternate including itself and the x-default. Purge any sitemap cache or CDN copy afterwards.

Step 5

Validate the sitemap structure

Open the sitemap in a browser to confirm it parses, and check that the urlset element declares the xhtml namespace, that every address is absolute, that ampersands are escaped, and that no single file exceeds 50,000 addresses or 50 MB uncompressed. Spot-check a few clusters: each address should appear once as a loc, and every entry in the cluster should carry the identical set of alternate links.

Step 6

Resubmit and recheck the clusters

Submit the sitemap or sitemap index in Search Console's Sitemaps report and confirm it is read without errors, and make sure robots.txt lists it. Run the Hreflang Checker across each locale again; the mismatch errors should be gone. Any remaining return tag, code or canonical errors were present in the source you kept, so fix those next. Repeat the comparison script after the next content release to confirm nothing drifts.

Questions and answers

1. Is it wrong to have hreflang in both the sitemap and the HTML?

It is allowed, and Google treats the methods as equivalent. It only becomes a problem when the two disagree, which tends to happen because they are generated by different tools at different times. If they are produced from the same data and always match, having both does no harm, but it doubles what you have to check. Most sites are better off choosing one source and removing hreflang from the other, so there is only one thing to maintain.

2. Which does Google use if the sitemap and HTML disagree?

Google has not published a rule for which source wins when they conflict. It may combine them, prefer one, or treat the relationship as unreliable, and the result can differ from page to page. That uncertainty is the reason to fix the mismatch rather than wait: while two versions of the cluster exist, you cannot predict which version of your page will be shown in each market. Make them identical, or better, keep only one.

3. Do I need the xhtml namespace in my sitemap?

Yes. The xhtml:link entries only mean something to a sitemap reader when the urlset element declares the namespace with xmlns:xhtml set to http://www.w3.org/1999/xhtml. Without it the alternate links are not recognised, and in some parsers the file fails altogether. It is a common omission in hand-built sitemaps and in generators where hreflang support was added later. Check the opening urlset element of every sitemap file that carries alternates, including files produced by plugins.

4. Does every URL in a hreflang sitemap need all the alternates?

Yes. Each loc entry must list every version in its cluster, including itself and the x-default if you use one. The sitemap equivalent of a page's head block is the set of links under that page's entry, and the same rules apply: self-reference, reciprocity and valid codes. A sitemap that lists the alternates only under the English entry, and leaves the German and French entries bare, has missing return links for every translated page.

5. Will the Search Console Sitemaps report show my hreflang errors?

No. The Sitemaps report shows whether Google could fetch and read the file and how many pages it found, not whether the hreflang inside it is valid or consistent with your pages. Search Console's International Targeting report, which once listed hreflang errors, has been retired. To find mismatches between the sitemap and the HTML, or errors within the sitemap's clusters, you need a crawler that reads both, such as the Hreflang Checker, or your own comparison script.

6. Can alternates in the sitemap point to another domain?

Yes. A cluster can span country domains such as example.co.uk and example.de, and a sitemap on one domain can list alternates on another. For Google to accept a sitemap that includes addresses on other hosts, you need to have verified ownership of those hosts in Search Console. Many multi-domain sites avoid the question by giving each domain its own sitemap containing its own addresses as loc entries, each with the full set of cross-domain alternates.

7. Our sitemap is getting huge because of all the alternate links. What can we do?

Split it. A single sitemap file can hold up to 50,000 addresses and 50 MB uncompressed, and alternate links count towards the size, so a site with many locales reaches the size limit sooner. Break the sitemap into several files, for example one per section or per locale, and list them in a sitemap index. You can also gzip the files. Alternatively, if the size is unmanageable, moving hreflang to the HTML head is a valid option.

Compare your sitemap and page hreflang

The Hreflang Checker follows each cluster of locale pages and validates return tags, language codes, x-default, sitemap consistency, target reachability and canonical alignment, so differences between your sitemap and page annotations are reported cluster by cluster.

Open Hreflang Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Hreflang Fixes  ·  Adding Missing Self-Referencing Hreflang  ·  Sitemap Mismatches  ·  Hreflang Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.