Guide · Internationalisation
hreflang: the complete cluster, on every page
hreflang serves one purpose only: telling a search engine that several addresses carry the same content in different languages, and which one to show to whom. It is a collective declaration: it belongs to the entire group, page after page. It takes full effect only once every page in the group carries the complete list. We learned this on our own site.
Four rules, and they are enough
hreflang reads in plain terms, rule by rule. The four rules below can be verified from the page as served, on the sole evidence of what it renders, and the fourth is almost always the one still left to put in place.
01
Reciprocity
If page A declares page B, page B must declare page A. A search engine retains a declaration when both sides render it: mutual confirmation is what gives it its force, and that is what the engine relies on.
See it: Take two versions of the same page and read their declarations side by side. Each must cite the other, with the exact address served by the other — same protocol, same domain, same trailing slash.
Putting it in place: Generate the declarations from one single list of versions, in one action for all pages rather than by hand. A single list makes reciprocity automatic.
02
Every page declares itself
The group is read from the point of view of any one of its pages. A page that cites itself in addition to its translations makes the group complete, as seen from all the others.
See it: On the French version, look for the hreflang="fr" declaration. It must be there, and point to the page you are currently reading.
Putting it in place: The page's own language is part of the cluster just like the others. It is the declaration most often forgotten, because it looks redundant — yet it carries exactly as much weight as the rest.
03
x-default
x-default designates the version served to a visitor whose language falls outside the declared languages. It is the group's fallback: it occupies a declaration in its own right and points to one of the existing versions.
See it: Count them: one declaration per language served, plus one for x-default. On our site, eight languages plus x-default make nine declarations per page.
Putting it in place: Choose a fallback version and designate it the same way everywhere. x-default goes on every page in the group, exactly like the others.
04
The partial cluster — the most frequent error
This is our own incident, recorded on 1 September 2026 in our Search Console: pages "crawled, currently not indexed", on a site that was otherwise technically sound. Each page declared only part of the group's versions. The Dutch page announced fr and nl; the French page, fr and en. Six languages out of eight remained to be hooked back into the network.
See it: Search engines stay silent about this: it's the count that tells the story. Count the tags on a translated page, then compare that list with the one on another page in the group — the two lists must be identical.
Putting it in place: The COMPLETE cluster on EVERY page, French included — French, the seven other languages, then x-default. The operation is idempotent: the old declarations are removed before the new ones are put in place, so that republishing always leaves a single generation of tags in position.
What a page on this site declares
The site is served in eight languages. Every one of its pages therefore carries the same list, in the same order, whichever language you happen to be reading — plus x-default, which designates the version served to a visitor whose language falls outside the eight declared ones.
- fr English
- en English
- es Spanish
- de German
- it Italian
- pt Portuguese
- nl Dutch
- pl Polish
- x-default Fallback
Nine declarations per page, identical from one version to the next. The French page stands on equal footing with all the others: it carries the same list as the Polish one.
Where to place the declarations
Three placements are valid, and any one of them is enough:
- In the page's <head>. One element per version, each with its language and its absolute address. This is the easiest placement to verify: the declaration is readable straight from the HTML response.
- In the HTTP header. The only placement available to a document served outside HTML — a PDF, for example.
- In the sitemap. Each URL entry carries the group's versions in xhtml:link. The advantage is volume: the entire group is declared once, in a file you control.
We place them in the <head> AND in the sitemap. The two placements are read independently: if one is read before the other, the group is already known.
How to check
The verification comes down to three steps, and the first is a count. Request the page as served, count its hreflang declarations, and compare that number to the number of languages you serve, plus one for x-default. A discrepancy of even a single unit means a partial cluster.
Run the count again on a second version of the same page: the two lists must be identical, address for address. Finally, follow one declared address and check that it returns the reverse declaration. All three steps fit within the HTML response alone — your browser is enough, exactly as it is already installed.
Since our incident, our publication guard only lets a page through once it declares exactly as many hreflang tags as there are languages served, plus x-default. The check happens at publication time: whatever goes live carries the complete cluster.
Count your submissions from your browser
The Checker reads your address and returns the state of your signals, free of charge, in about ten seconds. Your site remains exactly yours: everything happens on read, on our side. The Audit SEO Records goes further: it reads your entire sitemap, page by page, and hands you the dated findings. And if you want the signals corrected at the moment the page is served, SEO Records does it from €199 per year.