Hreflang errors: the ones that break the whole set
Why one error affects the whole set
A set of pages is a set of claims about each other, and a claim only one party makes is not usable by anybody.
This is the property that makes hreflang unusual to debug. A broken image affects one page. A page missing from the set breaks the relationship for every other member, because each of them now names something that does not name it back. The fault is in a pair, but the damage is in the group.
The practical consequence: the page that breaks the set is almost never the page anybody is looking at when the problem is reported.
The declaration that is never returned
The commonest fault by a wide margin, and the one worth checking first.
Page A names page B as its alternate. Page B does not name A. The relationship is asserted by one side only, and there is no way for anybody receiving A's claim to confirm it, so the claim does not do the work it was written to do. Every page in a set must name every other, and must name itself.
Four maintenance patterns produce this, and none of them looks like a mistake at the time:
Adding a language
The new pages list every existing version, and the existing versions are not edited to list the new one. The new pages are correct in isolation; the set is not.
Removing a page
The other members keep naming an address that no longer exists or no longer carries the declarations. Everyone remembers to delete the page and nobody remembers the references to it.
Generating one side from a template
A template that emits the set on some page types and not others produces a set whose membership depends on which template rendered the page, which is invisible until you compare pages of different types.
Editing one region's pages independently
Where different teams own different markets, each team maintains its own side of the relationship, and the sides drift.
The check that finds all four is the same one described below: fetch each declared address and read what it declares back.
Entries that point at the wrong thing
The address in a declaration is a promise, and three kinds of address break it.
Addresses that redirect
A declaration naming an address that then sends the client elsewhere is claiming a relationship with a page that is not there. The relationship you meant is with the destination, so name the destination. Where the destination itself redirects, the entry is pointing into a chain, and every hop in it is one more place the set can come apart.
Addresses that do not return the page
Entries left pointing at removed pages, at error responses, or at addresses excluded from being fetched by your own configuration. Each of these is a member of the set that cannot participate in it.
Addresses from another environment
Staging hostnames are the classic case, usually arriving through a template that was correct in the environment where it was written. These are worth checking explicitly, because they look completely normal to anybody reading the page in the environment where they work.
Two methods disagreeing
The declarations can live in the page, in a sitemap, or in an HTTP header, and the trouble starts when two of those are in use at once.
Nothing forbids using more than one method. What causes damage is that they are maintained by different people at different times: the pages are edited during a redesign, the sitemap is generated by a system nobody has looked at in a year, and the two now describe different sets. Which one is believed is not a question with a satisfying answer, and the honest fix is not to be in that position.
Pick one method, remove the others, and make that choice explicitly rather than by accident. Where a site has drifted into two, the older one is usually the sitemap, and it is usually the one still describing the structure the site had two redesigns ago.
Conflicts with the canonical declaration
Strictly from the hreflang side: a set can be undermined by what its members say about their own canonical address.
The rule that avoids the conflict is that each version's canonical declaration names itself. When a version instead names another version as canonical, the page is saying two incompatible things at once: that it is a distinct version for a distinct audience, and that it is a duplicate of a different page. The second statement disagrees with the whole point of the first.
This is a frequent outcome of consolidating duplicates, where somebody correctly removes duplication across a site and applies the same treatment to translations, which are not duplicates. Depth on canonical belongs elsewhere; what matters here is only the interaction: the set requires each member to stand as its own address.
How to check
The check is mechanical, and it is the only one that finds the faults above.
For each page in the set, take every address it declares, fetch it, and read the declarations that come back. Three questions settle it: does the address return the page directly rather than redirecting, does the page name your original page in return, and does it name itself. A set that passes all three for every pair is correct regardless of which method carries the declarations.
Doing this by hand is feasible for a handful of pages and unreasonable for a large set, which is what a validator that follows and re-reads each address is for: a mechanical pass over the same three questions, one address at a time.
Two checks are worth adding to the same pass: that every language code is a valid ISO 639-1 value and every region a valid ISO 3166-1 one, and that the fallback entry, if the set has one, appears in exactly one place.
What to fix first
In this order, because the first two are what break sets and the rest are tidying.
Non-returned declarations come first: they are the most common and they damage the whole group. Addresses pointing at redirects or missing pages come second, since a member that cannot be fetched cannot participate whatever else is correct. Method disagreements come third, and are usually resolved by deleting rather than editing. Code and fallback problems come last: they narrow or widen who a page is offered to, which matters, but they do not stop the set from working.
Recheck after each change rather than at the end. Sets are edited in more than one place, and a fix applied to one side is a change to the pair rather than to the page.
FAQ
Why does one missing page break the whole set?
Because every other member names it, and each of those claims is now unreturned. Hreflang describes relationships between pairs, so removing a member does not subtract one page from the group; it leaves every remaining page holding a claim that nothing confirms.
Do I have to list a page in its own set?
Yes. Each page names every member including itself, which is what makes the set consistent from whichever member an engine reads it. A set where members name each other but not themselves is incomplete in the same way as one that misses a member entirely.
Can I use both page markup and a sitemap?
Nothing forbids it, and it is still worth avoiding. Two methods are maintained by different people on different schedules and eventually describe different sets, at which point the question of which is believed has no answer you can act on. Pick one and remove the other.
Should each version point its canonical at the main language?
No. Each version's canonical names itself, or the page is claiming to be a duplicate of another version while also claiming to be a distinct version for a distinct audience. That combination is the most common way a correctly built set stops working.
Hreflang, claim by claim
A language set is a set of claims, and this section reads each one, faults included. The platform these pages describe is Bridge CDN, where an account is created.