Hreflang Sitemap: xhtml:link Syntax and Setup Guide

By Aoife Brennan ·

The Hreflang Sitemap: Format, Example, and When to Use It

A hreflang sitemap is an XML sitemap that carries your hreflang annotations as xhtml:link elements inside each <url> block, instead of (or alongside) <link> tags in the page's <head>. It is not a shortcut and it is not "hreflang for people who don't want to touch templates." It is the exact same five-rule contract you already know from HTML hreflang, written into a file your build probably generates anyway.

This article assumes you already know what hreflang does and why return links matter. If you need that groundwork first, our hreflang tags explained guide covers the rule set from scratch. Here, we're answering a narrower, more practical question: what does sitemap xml hreflang actually look like on the wire, and when should you use it instead of tags in the page itself?

What xhtml:link Looks Like Inside a <url> Block

Standard XML sitemaps only understand a handful of elements: <loc>, <lastmod>, <changefreq>, <priority>. Hreflang isn't part of that core vocabulary, so it rides in as an extension. This is the xhtml:link hreflang syntax you'll be writing for every localized URL, and it starts with a namespace declaration most tutorials skip straight past.

The sitemaps.org protocol explicitly allows extensions like this: you can extend a sitemap "using your own namespace," declared once on the root <urlset> element (sitemaps.org protocol). For hreflang, Google borrows the XHTML namespace, so every hreflang sitemap starts with this line on the root element:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml">

Skip that second xmlns:xhtml attribute and you've got a problem a validator will flag immediately: an undeclared namespace prefix. Here's what that looks like broken:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://www.example.com/us/en/widgets</loc>
    <xhtml:link rel="alternate" hreflang="en-gb" href="https://www.example.com/uk/en/widgets"/>
  </url>
</urlset>

A strict XML parser will throw a namespace error here, because xhtml: is used without ever being declared. A more forgiving parser might just skip the attribute quietly. Either way, Google's own documentation is specific that the namespace must be declared on the root element before xhtml:link entries are read.

"The tags may be ignored" is the honest way to describe the outcome, since behaviour varies by what's reading the file. I've seen both a hard parse failure and a silent skip on real sites, depending on whether it was a strict validator or a crawler being generous.

Once the namespace is in place, each variant of a page gets one line:

<xhtml:link rel="alternate" hreflang="en-gb" href="https://www.example.com/uk/en/widgets"/>

rel="alternate" never changes. hreflang takes the same language and region codes you'd use in an HTML tag, lowercase language code, optional region or script code after a hyphen, or x-default for the fallback. href has to be the fully-qualified, final URL, transport method included, not a relative path and not a URL that redirects or canonicalises elsewhere (see our guide to hreflang and canonical conflicts).

A complete hreflang sitemap example

The part every competing tutorial skips is the bit that actually matters: what the return links look like once you write out more than one block. A single <url> snippet in isolation tells you nothing about whether the reciprocity rule is satisfied, because reciprocity is a property of the whole cluster, not of one entry.

Say you run an English/US, English/UK and German storefront sharing one product catalog, with the US page also acting as the x-default fallback (see our hreflang x-default guide if you're unsure which page should carry that role). Here's the complete, three-locale hreflang sitemap example, all three blocks included:

<?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://www.example.com/us/en/widgets</loc>
    <xhtml:link rel="alternate" hreflang="en-us" href="https://www.example.com/us/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="en-gb" href="https://www.example.com/uk/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="de-de" href="https://www.example.com/de/de/widgets"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://www.example.com/us/en/widgets"/>
  </url>

  <url>
    <loc>https://www.example.com/uk/en/widgets</loc>
    <xhtml:link rel="alternate" hreflang="en-us" href="https://www.example.com/us/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="en-gb" href="https://www.example.com/uk/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="de-de" href="https://www.example.com/de/de/widgets"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://www.example.com/us/en/widgets"/>
  </url>

  <url>
    <loc>https://www.example.com/de/de/widgets</loc>
    <xhtml:link rel="alternate" hreflang="en-us" href="https://www.example.com/us/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="en-gb" href="https://www.example.com/uk/en/widgets"/>
    <xhtml:link rel="alternate" hreflang="de-de" href="https://www.example.com/de/de/widgets"/>
    <xhtml:link rel="alternate" hreflang="x-default" href="https://www.example.com/us/en/widgets"/>
  </url>

</urlset>

Look at the first block. The en-us line pointing back at /us/en/widgets is a self-reference, the same rule our self-referencing hreflang guide covers in more depth: Google's guidance is explicit that each URL should list itself as one of its own alternates. The en-gb and de-de lines are the actual cross-references, and they're what turns three separate pages into one cluster.

Now look at the second and third blocks: each one lists the other two locales too. That's the return link, made visible. If the UK block didn't list the US page, or the German block didn't list the UK page, the cluster would be incomplete, and Google's documented behaviour on that is blunt: if two pages don't both point to each other, the tags will be ignored.

Here's that failure mode written out, so you know what it looks like broken instead of fixed:

<url>
  <loc>https://www.example.com/us/en/widgets</loc>
  <xhtml:link rel="alternate" hreflang="en-us" href="https://www.example.com/us/en/widgets"/>
  <xhtml:link rel="alternate" hreflang="de-de" href="https://www.example.com/de/de/widgets"/>
</url>

<url>
  <loc>https://www.example.com/de/de/widgets</loc>
  <xhtml:link rel="alternate" hreflang="de-de" href="https://www.example.com/de/de/widgets"/>
</url>

The US block points to the German page, but the German block never points back. A hreflang checker reading this pair would flag no_return_link on the US side and cluster_incomplete on the cluster overall, because the German page never named the US page as an alternate. In my experience this is the single most common hreflang sitemap bug, and it's almost always caused by one locale's URL changing, a slug edit, a redesign, a migrated path, without the sitemap generator re-running for every other locale sharing that cluster.

How the five hreflang rules translate to XML

The rules themselves don't change between HTML and sitemap hreflang. Only the syntax expressing them does. Here's the mapping, with the issue codes a hreflang checker reports when a rule breaks:

RuleHTML formSitemap formChecker flags when broken
Self-referencePage links to itself in its own headThe URL's own url block lists itself as an xhtml:linkno_self_reference
Return linksEvery alternate page links backEvery url block in the cluster lists every other memberno_return_link, cluster_incomplete
Fully-qualified URLshref is absolute, not relativexhtml:link href is absolute, not relativerelative_hreflang
x-default fallbackOne link tag uses hreflang="x-default"One xhtml:link line uses hreflang="x-default"no_x_default, cluster_no_x_default
Valid language and region codeshreflang attribute uses ISO codeshreflang attribute uses ISO codesbad_code, suspect_code, code_case

Nothing here is unique to sitemaps, which is really the whole point. If you've already got HTML hreflang working correctly, moving it into a sitemap is a formatting exercise, not a redesign of your logic.

Sitemap vs HTML tags vs HTTP header

Google states plainly that the three methods, HTML <link> tags, the Link HTTP response header, and sitemap xhtml:link entries, are equivalent from a ranking and processing standpoint. None is treated as a stronger signal than the others. Where they differ is practical, not algorithmic:

Where it livesHTML tagsHTTP headerSitemap xhtml:link
LocationInside the page's headA response header, invisible in the page sourceA separate XML file
Who edits itWhoever owns the templateWhoever owns the server or CDN configWhoever owns the sitemap generator
Adds page weightYes, a few lines per alternateNo, doesn't touch the HTML bodyNo, lives outside the page entirely
Works on non-HTML filesNo, a PDF has no head to hold tagsYes, headers work on any content typeYes, a sitemap entry doesn't care what the file is
Visible to third-party crawlersYes, most tools read HTML by defaultOnly if the tool checks headersOnly if the tool fetches and parses the sitemap
When a page's URL changesEvery related page's template needs the new URLEvery related response needs the new URLOne file needs regenerating

That last row is the real argument for the sitemap route on large sites. Centralising the cross-references in one generated file is usually easier to keep accurate than templating them into every page's head, especially once you're past a handful of locales.

When the sitemap method is the right call

A plain decision list, because "it depends" isn't much use on its own:

  • You're running eight or more locales, and the cluster math (N locales means N blocks with N lines each) is already getting unwieldy to hand-place in templates.
  • Your CMS doesn't give you head-tag control per template, but you do control the sitemap generation step in your build.
  • You need hreflang on non-HTML files, PDFs being the obvious case, where a link tag has nowhere to live.
  • Your build pipeline already produces the sitemap programmatically, so adding xhtml:link entries becomes a data problem rather than a new system to stand up.

And when it's the wrong call:

  • You run a small site with two or three locales. Four lines of HTML per page is genuinely less work than standing up a sitemap hreflang pipeline for it.
  • Your sitemap isn't reliably regenerated on publish. A sitemap hreflang setup that goes stale the day after launch is worse than tags that update automatically with the page itself.
  • You're on a platform that locks the auto-generated sitemap. Shopify is the clearest example: its default sitemap does not include hreflang, and Shopify does not support editing that file directly (Shopify's own blog). Store owners in that position either host a separate, manually-maintained sitemap or fall back to HTML tags through a theme edit or app.

Mixing sitemap and HTML hreflang

You can run all three methods on the same site at once. Google's documentation says as much, and adds there's no ranking benefit to doing so, only more surface area to keep in sync.

In a 27 November 2020 Search Central livestream, John Mueller went further and described what actually happens when a sitemap and the HTML disagree: Google will "try to combine that and add that together," but where the same language or region code points at two different URLs across the two sources, "we would probably drop that pair," as reported by Search Engine Journal. Consistent duplication across both sources is harmless. It's also pointless, since it doesn't add anything Google isn't already getting from one of them.

The practical rule I give clients: pick one source of truth. If you're mid-migration and genuinely need both running for a while, the two need to stay identical on the URLs they name, or you're actively creating the conflicting-pair scenario Google says gets dropped.

Size, splitting, and cross-domain hosting

A quick note on the numbers, since a hreflang sitemap can balloon fast. Google's own child-element rule works in your favour here: child elements don't count toward the URL limit for sitemaps. So 10,000 product pages across twelve locales is still 10,000 <url> entries against the 50,000-URL cap, even though each block might carry twelve xhtml:link lines.

What does count against you is raw file size. The sitemaps.org protocol caps every sitemap file at 50MB uncompressed, and those extra lines add real bytes. Twelve locales times twelve lines per block adds up fast on a large catalog, and Google's own limits page confirms the same 50MB / 50,000-URL ceiling applies here too (Google: Build and submit a sitemap).

If you're bumping into that ceiling, a sitemap index file is the standard fix, and there's a hosting rule worth knowing before you set one up: sitemaps referenced from an index generally need to be hosted on the same site as the index file itself, unless you've explicitly configured cross-site submission (Google: Manage large sitemaps). That rule governs where the sitemap file lives, not where the hreflang alternates point.

Cross-domain hreflang is explicitly fine; Google's own example pairs an English page on one domain with a German one on a separate ccTLD. But a German sitemap covering the German domain still needs to be one Google accepts as belonging to that property. General sitemap architecture, splitting strategy and index files sit outside what this article covers; our sister site sitemapcheck.tools goes deeper on sitemap indexing and large-site sharding.

One encoding note that trips people up more than it should: sitemap files must be UTF-8, and any ampersand in a URL has to be escaped as &amp;, same as apostrophes and angle brackets. If your alternates carry query strings, which a lot of faceted or campaign URLs do, that's usually where a hreflang sitemap first breaks XML validity.

Checking it after you publish

Here's the part that's genuinely gotten harder since 2022. Google's International Targeting report, the one screen in Search Console that used to list hreflang errors directly, was removed after 22 September 2022. Google confirmed at the time that hreflang support itself wasn't going anywhere, just that particular report. What's left in Search Console is the Sitemaps report, which tells you whether Google has read the file at all (check the "Last read" date; a sitemap Google hasn't fetched yet contributes nothing, hreflang or otherwise), and the URL Inspection tool, which confirms what Google saw on one URL at a time but won't hand you a list of broken clusters.

That gap is exactly why we built this site's checker: paste a sitemap URL into the hreflang.tools checker and it reads every xhtml:link entry, fetches the alternate targets, verifies the return links actually exist on both sides, and groups pages into clusters so an incomplete one shows up at a glance instead of something you have to trace by hand across dozens of blocks. Worth being upfront about what it can't do, listed on the about page: it reads what your sitemap and pages serve right now, it doesn't run JavaScript, and it can't tell you what Google has actually indexed. That last part still needs Search Console. What it can tell you is whether the file itself would pass Google's stated rules, which is usually the first thing worth ruling out before you start guessing at indexing behaviour.

Frequently Asked Questions

How do I add hreflang to an XML sitemap?

Declare the XHTML namespace on your sitemap's root urlset element, xmlns:xhtml="http://www.w3.org/1999/xhtml", then inside every <url> block add one xhtml:link line per language variant, including the page's own locale. Every URL that belongs to the cluster needs its own <url> block listing all the others, and every href has to be a fully-qualified, non-redirecting URL.

Is sitemap hreflang better than HTML link tags?

No. Google states the three methods, HTML tags, the HTTP header, and sitemap entries, are equivalent from a Search perspective, so "better" isn't really the right frame. The real question is which is easier for your site to keep accurate: HTML tags suit small sites with template control, while a sitemap tends to be easier to manage at scale because the cross-references live in one generated file instead of being templated into every page.

Do I still need return links in a sitemap?

Yes, exactly as in HTML. Google's guidance is that if two pages don't both point to each other, the tags get ignored, and that rule doesn't relax just because the annotations moved into a sitemap. In practice this means every locale in a cluster needs its own <url> block, and each block has to list every other locale, not just the one or two it happens to link to first.

Can I mix sitemap hreflang with HTML hreflang?

You can run both at once, and Google has said it will try to combine the signals. The catch is what happens when they disagree: if the sitemap and the HTML name different URLs for the same language or region code, Google has indicated it will likely drop that conflicting pair rather than guess which one is right. If you must run both during a migration, keep the two sources identical instead of treating one as a rough draft of the other.