Technical SEO
How to Add Hreflang in Your XML Sitemaps (Step by Step)
How do you add hreflang in an XML sitemap?
You add hreflang to an XML sitemap by giving each page its own
<url>entry and, inside it, a set of<xhtml:link rel="alternate" hreflang="...">child elements — one for every language/region variant of that page, including the page itself. The sitemap’s root<urlset>must declare the XHTML namespace (xmlns:xhtml="http://www.w3.org/1999/xhtml"), and every variant’s<url>entry carries the same complete set of alternate links. It is one of three methods Google accepts for declaring hreflang; the other two are<head>link tags and an HTTPLink:header. All three are equivalent, so the sitemap simply moves the same annotations out of your page markup and into a single file you can generate and validate on its own.
I do both sides of this — I write the code and I do the SEO — and the sitemap method is the one I reach for whenever hreflang has to scale past a handful of hand-edited pages. It keeps the language graph in one file instead of scattered across every template’s <head>, which makes it far easier to generate programmatically and to diff when something breaks. This guide is about that method specifically: the exact xhtml:link syntax, why teams pick it over head tags, and the validation errors that quietly get the whole set ignored. For building and validating hreflang across hundreds of programmatic URLs the multilingual SaaS implementation guide covers the generation machinery; this one stays on the sitemap format itself.
TL;DR — Key takeaways
-
The sitemap is one of three equal ways to declare hreflang. Google treats HTML tags, HTTP headers, and the sitemap as equivalent and is explicit that using all three at once gives no Search benefit — pick one. The sitemap wins when you want the language graph in a single generated, validatable file rather than in every page’s
<head>. -
Every
<url>entry lists every variant, including itself. Google’s rule is that each language version must list itself as well as all other language versions. If you have three locales, every one of the three<url>entries carries the same three<xhtml:link>children. -
The XHTML namespace is mandatory. The
<urlset>must declarexmlns:xhtml="http://www.w3.org/1999/xhtml". This is a standard namespace extension of the base sitemap protocol, which explicitly allows extending the format through your own namespace. -
Reciprocity is enforced. If two pages don’t both point to each other, the tags will be ignored — a deliberate rule so nobody else can claim your page as their alternate. A one-directional entry is a dead entry.
-
The alternate links are free against your URL budget. A sitemap is capped at 50,000 URLs and 50MB uncompressed, but Google confirms the child
<xhtml:link>elements don’t count toward that URL limit — only the<loc>entries do. -
Most failures are code or escaping mistakes. Region-only codes like
uk, unescaped&in URLs, relativehrefs, and missing return links are the errors I see most, and none of them throw a loud alarm — the annotations just silently stop working.
Why declare hreflang in the sitemap instead of the <head>
Google is unusually direct that this is a free choice. There are three ways to indicate language and region variants — <head> link tags, an HTTP Link: response header, and the XML sitemap — and the documentation states the three are equivalent from Google’s perspective, adding that while you can use all three at once, there is no Search benefit and it is usually harder to manage three implementations than to pick one. So the decision is purely operational.
The sitemap method earns its place in three situations:
-
You cannot easily touch the
<head>. On many stacks the<head>is templated, cached, or owned by a team that isn’t yours. A sitemap is a separate artifact you generate and deploy on your own cadence, without editing page markup at all. -
The set is large or programmatic. Putting the full alternate block in the
<head>means every page in an n-locale cluster ships n extra link tags on every request. The sitemap consolidates the entire language graph into one file, which is dramatically easier to generate from a database and to validate as a whole. This is the crossover point I describe in the hreflang-at-scale guide. -
You want the mapping auditable in one place. When hreflang lives in a single file you can diff it, run it through a validator, and reason about reciprocity across the whole site at once — instead of viewing source on hundreds of pages.
The one thing the sitemap method does not cover is non-HTML files. For declaring alternates on a PDF, Google points you to the HTTP Link: header method instead, since a PDF has no <head> and won’t appear as an HTML <url> you’d annotate. For ordinary HTML pages, the sitemap is the cleanest of the three.
One thing worth setting straight up front: hreflang is a targeting signal, not a language detector. Google states plainly that it does not use hreflang or the HTML lang attribute to determine a page’s language — it uses its own algorithms for that. The sitemap tells Google which URLs are variants of each other so it can serve the right one per audience; it does not tell Google what language any page is written in.
The anatomy of an hreflang sitemap entry
Start from Google’s own worked example. Say one page exists in three variants: English for a worldwide audience, German for a worldwide audience, and German for Switzerland specifically. Here is the sitemap Google documents for exactly that case:
<?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/english/page.html</loc>
<xhtml:link rel="alternate" hreflang="en"
href="https://www.example.com/english/page.html"/>
<xhtml:link rel="alternate" hreflang="de"
href="https://www.example.de/deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="de-ch"
href="https://www.example.de/schweiz-deutsch/page.html"/>
</url>
<url>
<loc>https://www.example.de/deutsch/page.html</loc>
<xhtml:link rel="alternate" hreflang="en"
href="https://www.example.com/english/page.html"/>
<xhtml:link rel="alternate" hreflang="de"
href="https://www.example.de/deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="de-ch"
href="https://www.example.de/schweiz-deutsch/page.html"/>
</url>
<url>
<loc>https://www.example.de/schweiz-deutsch/page.html</loc>
<xhtml:link rel="alternate" hreflang="en"
href="https://www.example.com/english/page.html"/>
<xhtml:link rel="alternate" hreflang="de"
href="https://www.example.de/deutsch/page.html"/>
<xhtml:link rel="alternate" hreflang="de-ch"
href="https://www.example.de/schweiz-deutsch/page.html"/>
</url>
</urlset>
Four properties of that structure are the whole method:
-
The namespace declaration is required. The
<urlset>carries both the standard sitemaps namespace andxmlns:xhtml="http://www.w3.org/1999/xhtml". Without the second declaration thexhtml:linkelements are undefined. The base protocol explicitly supports this kind of namespace extension — you declare a namespace in the root element and add its tags inside<url>. -
One
<url>per page, each with a<loc>. You create a separate<url>element for each URL as you would with any other sitemap, and each must have a<loc>child naming the page. -
The alternate block is complete and self-inclusive. Each
<url>entry lists every variant including itself. That’s why the three-locale example produces three<url>entries, each with three identical<xhtml:link>children. Note the English entry references its own English URL, the German entry references the German URL, and so on — every version lists itself. -
Order doesn’t matter, and the children are free. Google states the order of the child
<xhtml:link>elements doesn’t matter, though keeping them in a consistent order makes mistakes easier to spot. Crucially, those child elements don’t count toward the sitemap’s URL limit — a 40,000-URL multilingual sitemap doesn’t blow the cap just because each entry carries five alternates.
If you want the human-readable fallback for users whose language you don’t support, add an x-default alternate to the block: <xhtml:link rel="alternate" hreflang="x-default" href="..."/>. Google describes x-default as the reserved value for when no other language/region matches the user’s setting, and it works best pointing at a language-selector or generic landing page.
Step by step: building the sitemap correctly
-
Declare both namespaces on
<urlset>. The standardhttp://www.sitemaps.org/schemas/sitemap/0.9plusxmlns:xhtml="http://www.w3.org/1999/xhtml". Miss the XHTML one and every alternate is ignored. -
Emit one
<url>block per page, with its<loc>. Keep<loc>under the protocol’s hard limit — the<loc>value must be less than 2,048 characters. -
Attach the full, self-inclusive alternate set to every entry. Generate the alternate block once per cluster and stamp the identical set into each member’s
<url>. Every entry, including the page’s own, must appear in it. -
Use fully-qualified, absolute URLs everywhere. Google requires alternate URLs be fully-qualified including the transport method —
https://example.com/foo, never//example.com/fooor/foo. The alternates do not have to sit on the same domain, so a ccTLD-per-market setup is fine. -
Entity-escape the URLs. This is the sitemap-specific trap. The protocol requires that all data values, including URLs, use entity escape codes — most importantly
&becomes&. A raw ampersand in a parameterisedhrefbreaks the XML and can invalidate the file. -
Host the file where its URLs live. A sitemap can only contain descendant URLs of the directory it’s hosted from, and the protocol requires that all URLs in a sitemap come from a single host. Root-level hosting (
/sitemap.xml) is the safe default. -
Mind the size caps and submit. Keep each file within 50,000 URLs and 50MB uncompressed; beyond that, split across multiple files behind a sitemap index. Then tell search engines where it is — the protocol supports a
Sitemap:directive in robots.txt, and you can also submit it in Search Console.
The rules that make or break the file
The syntax is easy; the rules that get annotations silently ignored are where sets die. These four account for almost every broken hreflang sitemap I audit.
-
Return links must be reciprocal. Hreflang only works bidirectionally — if two pages don’t both point to each other, the tags are ignored. In the sitemap this means the German
<url>entry must reference the English URL and the English entry must reference the German URL. Because the sitemap generates the whole graph at once, this is actually easier to get right here than with hand-edited head tags — but a partial generation that emits one direction and not the other kills the pairing. -
Language and region codes must be valid. The value is a language code in ISO 639-1 format plus an optional region in ISO 3166-1 Alpha 2. Codes outside those standards — Google names
es-419— aren’t supported. And you cannot specify a country by itself:hreflang="uk"is not the United Kingdom, it’s a language-only code, and reserved elements likeEU,UN, andUKare ignored outright by Google Search. Useen-gb, neveruk. -
Unescaped ampersands corrupt the file. Parameterised URLs are the usual culprit. The protocol mandates entity escaping, so
?a=1&b=2must be written?a=1&b=2inside both<loc>and everyhref. This is invisible until a validator or Search Console rejects the sitemap. -
Relative or protocol-relative hrefs. The most common code-generation bug: emitting
/de/pageor//example.com/de/pageinstead of the fullhttps://…URL. Google’s fully-qualified-URL requirement has no exceptions.
Two more that bite at scale: cross-host confusion (each individual sitemap file must stay single-host, even though the alternates it references may live on other domains), and blowing the size cap on a large catalogue, which means splitting into a sitemap index. Both are mechanical to catch in generation.
Validating the sitemap before you rely on it
Because none of these errors raise an exception at request time, the sitemap has to be validated as an artifact. Two checks catch the vast majority of problems:
-
Schema and escaping. Run the file through an XML validator to confirm it’s well-formed and every value is properly entity-escaped. A single unescaped
&will surface here rather than in a month-late Search Console report. -
Hreflang-specific validation. Google itself points to third-party hreflang debugging tools — it names Merkle’s tag testing tool and Aleyda Solis’s generator — while noting they aren’t maintained by Google. These check the annotations themselves: reciprocity, code validity, and self-references across the set.
This is the same artifact-level discipline I apply across the crawl-and-indexing layer in the technical SEO audit methodology: validate the file that carries the signal, don’t wait for the ranking symptom. It matters most during a site migration, when URLs move and a stale or half-regenerated hreflang sitemap can quietly point half your alternates at the old structure.
Head tags vs. sitemap: which should you use?
Since Google treats the methods as equivalent, this is an operational call, not a ranking one:
-
Choose head tags for a small, stable set of pages where you control the
<head>and want the annotation to live right next to the content. Fewer moving parts, no separate file to keep in sync. -
Choose the sitemap when the set is large, programmatically generated, or when you can’t reliably edit every template’s
<head>. One file, generated from your source of truth, validatable as a whole. -
Don’t run both unless you have a reason. Google is explicit that using multiple methods together carries no Search benefit and adds maintenance cost. Two implementations means two things that can drift out of sync and disagree.
For a handful of pages the head-tag approach is often simpler. The moment the locale matrix turns into a generated table of hundreds of URLs, the sitemap is the method that stays maintainable — which is the whole subject of the multilingual SaaS hreflang guide.
FAQ
Do I put hreflang in the sitemap or in the page head?
Either — Google treats the sitemap, HTML <head> tags, and the HTTP header as equivalent and recommends picking just one. The sitemap is the better choice when the set is large or generated, or when you can’t easily edit every page’s <head>; head tags are simpler for a small, stable set you control directly.
What namespace does an hreflang sitemap need?
The <urlset> root element must declare xmlns:xhtml="http://www.w3.org/1999/xhtml" in addition to the standard sitemaps namespace. This is a namespace extension of the base sitemap protocol, which permits extending the format with your own namespace. Without that declaration the <xhtml:link> alternate elements are undefined and won’t be processed.
Does every URL entry need to include itself in the hreflang set?
Yes. Google’s rule is that each language version must list itself as well as all other versions. So a three-locale cluster produces three <url> entries, and each one contains the same three <xhtml:link> children — including a self-reference. Omitting the self-reference is a common reason a set is treated as incomplete.
Do the hreflang alternate links count toward the 50,000-URL sitemap limit?
No. A sitemap is capped at 50,000 URLs and 50MB uncompressed, but Google confirms the child <xhtml:link> elements don’t count toward that URL limit — only the <loc> entries do. A large multilingual sitemap won’t breach the cap purely because each page carries several alternates.
Why are my hreflang sitemap tags being ignored?
The usual causes are all silent. Missing return links — if two pages don’t both point to each other the tags are ignored; invalid codes such as a region-only uk when you can’t specify a country alone; relative instead of fully-qualified URLs; or unescaped ampersands, since the protocol requires all URL values to be entity-escaped. None throws a visible error, so validate the file directly.
Can hreflang alternates point to other domains in the sitemap?
Yes. Google states alternate URLs do not need to be on the same domain, so a ccTLD-per-market architecture works. The one constraint is that any single sitemap file must list URLs from a single host in its <loc> entries — but the alternates it references in <xhtml:link> may live elsewhere.
Next step
The hreflang sitemap is a single-file version of a language graph: declare the XHTML namespace, give every page a <url> entry with the complete, self-inclusive, reciprocal set of <xhtml:link> alternates, keep the URLs absolute and escaped, and validate the file before you trust it. Get those right and it scales to tens of thousands of URLs without touching a single page’s <head>.
For the same problem at programmatic scale — generating and validating this across a large locale matrix — see the multilingual SaaS hreflang guide; for the audit cadence that catches a drifting hreflang file before it costs traffic, the technical SEO audit framework; and when URLs are in motion, the site migration guide. If you’d rather have the international tag layer designed, generated, and monitored end-to-end, that’s part of what I do: GEO and technical SEO consulting covers hreflang, sitemaps, and the crawl-and-indexing interactions as one scope.