Royking Niba

hreflang Implementation: Google Says the Three Methods Are Equivalent, So the Choice Is an Engineering One

· Royking Niba

A printed world map with small national flags standing on several countries, photographed from above.

Most hreflang advice argues about which delivery method Google prefers. Google has already answered that, in writing, and the answer is that it does not have a preference. Its documentation on localized versions states that the three methods are equivalent from its perspective and that you can choose whichever is most convenient. Once you take that sentence seriously, the implementation question stops being an SEO question and becomes an engineering one: which method ships the fewest bytes, breaks in the fewest ways, and still works when you add the ninth locale. Below is the documentation in Google’s own words, then the arithmetic that actually decides it.

What Google documents, in Google’s words

Quoted from Google Search Central’s guidance on telling Google about localized versions of your page, last updated 21 September 2026, checked today.

The three methods are equivalent from Google’s perspective and you can choose the method that’s the most convenient for your site.

On choosing between HTML tags, HTTP headers and sitemaps

Each language version must list itself as well as all other language versions.

On what belongs in every set of annotations

If page X links to page Y, page Y must link back to page X. If this is not the case for all pages that use hreflang annotations, those annotations may be ignored.

On reciprocity

The first code of the hreflang attribute is the language code (in ISO 639-1 format) followed by an optional second code that represents the region code (in ISO 3166-1 Alpha 2 format).

On code format

The reserved x-default value is used when no other language/region matches the user’s browser setting.

On x-default

Two of those five sentences are the ones that get broken in practice. The self-listing rule means a set of six locales carries seven annotations on every page, not six, once x-default is included. The reciprocity rule means the failure mode is not a weaker signal but no signal at all: Google’s wording is that the annotations “may be ignored”, which is a cliff rather than a slope. A half-implemented hreflang set is not half as good as a complete one.

Note also what the page does not say. It gives no guidance on choosing between the three methods beyond convenience, and it says nothing about how hreflang interacts with rel=canonical. Anyone who tells you Google ranks one delivery method above another is not quoting the documentation.

The three methods, and what each one can reach

MethodWhere the annotations liveWorks on non-HTML filesWho downloads themNeeds a deploy to change
HTML link elementsThe head of every pageNoEvery visitor and every crawler, on every requestYes, a template change
HTTP Link headersThe response headers of every URLYes, including PDFsEvery visitor and every crawler, on every requestUsually a server or CDN config change
XML sitemapOne or more sitemap filesYesCrawlers onlyNo, regenerate the sitemap
Three methodsGoogle: equivalentTwo of threeOne of three costs nothing to humans

The third column is the reason the header method exists at all. An hreflang annotation cannot be put in the head of a PDF, so a site with localised documentation or localised price lists in PDF form has only two options for those files, and only one of them is cheap.

The worked example: six locales, 5,000 page sets

This is the dataset that decided it on the last international build I worked on. Six locales plus x-default is seven annotations per page. 5,000 page sets across six locales is 30,000 pages, and 30,000 pages at seven annotations each is 210,000 annotation lines that have to exist somewhere and stay reciprocal.

Where they exist is the whole decision. Assume a link element of roughly 98 bytes once the hreflang value and a realistic absolute href are written out, and a sitemap annotation line of roughly 100 bytes plus about 120 bytes of url and loc wrapper per URL.

RouteWeight per page or URLTotal weight that existsShipped to humansMonthly transfer at 1.2m page views
HTML head elements7 x 98 = 686 bytes in every page20.6 MB across 30,000 pagesYes, on every page load823.2 MB, about 0.82 GB
HTTP Link headersRoughly the same payload, in the headersComparableYes, on every requestComparable to the head route
XML sitemap120 + 7 x 100 = 820 bytes per URL entry24.6 MB in sitemap filesNo, crawlers onlyEffectively nil for visitors
Head route against sitemap routeSimilar one-off sizeAbout 33 times the monthly bytes

The two routes cost almost exactly the same to store, 20.6 MB against 24.6 MB. They differ by a factor of about 33 in what they cost to serve, because the head route bills the annotation weight to every human page load and the sitemap route bills it once to a crawler. Nothing in that 823 MB is read by a single visitor. It is routing metadata for machines, delivered down the one channel humans also pay for.

The head-weight curve, which is the part that bites later

Annotation count grows with locale count, so the head route gets worse every time the business opens a market. The same 1.2 million monthly page views, at different locale counts:

LocalesAnnotations per page, with x-defaultBytes added to every pageMonthly transfer at 1.2m page views
23294 B0.35 GB
56588 B0.71 GB
89882 B1.06 GB
12131,274 B1.53 GB
20212,058 B2.47 GB
30313,038 B3.65 GB
2 to 30 locales3 to 31Ten times the head weightTen times the transfer

Where the sitemap route runs out of room

The sitemap route has its own ceiling and almost nobody plans for it. Google’s documentation on building a sitemap, last updated 8 July 2026, is explicit:

All formats limit a single sitemap to 50MB (uncompressed) or 50,000 URLs.

On sitemap size limits

If you have a larger file or more URLs, you must break your sitemap into multiple sitemaps. You can optionally create a sitemap index file and submit that single index file to Google.

On exceeding the limits

Those are two limits, not one, and which of them binds depends on how many locales you have. Every hreflang annotation you add to a sitemap entry makes the entry heavier without adding a URL, so past a certain locale count the 50 MB cap starts biting before the 50,000 URL cap does. Using the same 820-bytes-at-seven-annotations basis:

LocalesAnnotations per URLBytes per URL entryURLs allowed by the 50 MB capWhich limit binds
23420 B119,047The 50,000 URL cap
56720 B69,444The 50,000 URL cap
891,020 B49,019The 50 MB byte cap
10111,220 B40,983The 50 MB byte cap
12131,420 B35,211The 50 MB byte cap
20212,220 B22,522The 50 MB byte cap
30313,220 B15,527The 50 MB byte cap
The crossover9 annotations1,020 BAbout 49,000At roughly 8 locales

At around eight locales the 50,000 URL limit stops being the number that matters. A sitemap generator that splits strictly on a 50,000 URL count, which is what most of them do, will quietly start emitting files over the uncompressed byte limit for any site with nine or more locales. At thirty locales the real ceiling is about 15,500 URLs a file, less than a third of the count-based split. That is a sitemap that validates on URL count and fails on size.

What these models assume

Both tables rest on assumed byte sizes: 98 bytes for a head link element, 100 bytes for a sitemap annotation line and 120 bytes of wrapper per URL entry. Real figures move with the length of your URLs, and a site with long localised slugs will be heavier than this and a site on short paths lighter. The monthly transfer figures also ignore gzip and Brotli, which will compress repetitive annotation blocks well, so treat 823 MB as the uncompressed upper bound rather than what crosses the wire. The 50 MB sitemap cap is explicitly an uncompressed limit, so compression does not rescue that one. Two conclusions survive all of the caveats: the head route is the only one that charges humans for machine metadata, and the byte cap overtakes the URL cap at around eight locales.

The reciprocity surface, which is what actually breaks

Google’s reciprocity rule is about pairs, and pairs grow with the square of the locale count. Six locales is six times five, so 30 ordered references per page set, 15 unordered pairs. Across 5,000 page sets that is 150,000 ordered references and 75,000 pairs that must all point both ways or the set may be discarded.

This is the structural argument for the sitemap route rather than the byte argument, and it is the stronger of the two. In the head-tag route those 150,000 references are generated by templates on 30,000 separately deployed pages, so a single locale whose template is behind by one release breaks reciprocity for every set it appears in. In the sitemap route all 150,000 live in files produced by one generator from one source of truth, which can be validated before publishing. You cannot diff 30,000 page heads in CI. You can diff a sitemap.

How I implement it

  • Default to the XML sitemap route for HTML pages, on the byte argument and the single-source-of-truth argument, not because Google prefers it. It does not.
  • Use HTTP Link headers only for the files that cannot carry a head element, which in practice means PDFs and other documents.
  • Split sitemaps on bytes as well as URL count, because past about eight locales the 50 MB uncompressed cap binds first and most generators only count URLs.
  • Validate reciprocity as a build step: every URL in the set must appear in every other URL’s annotation list, including its own. Google’s wording is that non-reciprocal annotations may be ignored entirely, so this is a pass or fail check rather than a score.
  • Check the code format against the two standards the documentation names, ISO 639-1 for language and ISO 3166-1 Alpha 2 for region, rather than against whatever the CMS locale picker produced.
  • Include x-default once per set, and make sure the page it points at is the one you actually want a non-matching browser to land on.

One thing worth separating from all of the above: none of this makes a page rank. hreflang decides which version of a page a matching user is shown, not whether the set ranks at all. The structural decision about where localised versions live, which is a different and larger question, is the one that moves traffic.

Related reading

Need help with your own site?

Tell me where it stands. I reply within a day, and the first look is free.

Send me a message

Goes straight to my inbox. I reply within a day.

Leave a Reply

Your email address will not be published. Required fields are marked *