← hreflang Reciprocity Check

hreflang Reciprocity Check vs. checking hreflang tags by hand

Reading a page's <link rel="alternate" hreflang> tags yourself is free and takes thirty seconds. It also cannot catch the two failures that break hreflang most often: a region code that looks right but was never assigned, and a reciprocal link that only fails to exist on a page you did not open.

What each approach is for

Checking by hand is fine for confirming a tag exists and is roughly the right shape — you will spot a missing href or an obviously mistyped attribute immediately. It cannot validate a region code against the actual ISO 3166-1 list from memory, and it cannot confirm reciprocity without opening every alternate page named on the one you started with and checking each one links back.

This tool validates every hreflang value's language and region subtags against the real BCP 47 / ISO 3166-1 data, checks that no two tags declare the same code for different URLs, flags a missing self-reference or x-default, and — given the alternate pages' content, either fetched by the CLI or pasted into the browser page — confirms each alternate actually links back. It runs free, with no account, and the browser page sends nothing to a server.

What checking by hand catchesWhat this tool catches too
A tag with no href, or an obviously misspelled attribute.Same, plus every hreflang value validated against the real language/region tables.
"en-uk" looks like a reasonable code for British English.Flags it: "UK" was never assigned an ISO 3166-1 region code; the real one is "GB".
Whether the page you are reading lists itself among its own alternates — if you think to check.Always checks self-reference, mechanically, on every run.
Whether every alternate page links back — only if you open all of them and compare by hand.Confirms reciprocity for every alternate given its content, and reports "unverified" rather than guessing when content isn't available.
Two same-code tags pointing at different URLs, if you notice the duplicate on the same page.Always flags conflicting or duplicate hreflang codes.

A hreflang set that looks complete

Four languages, four tags, every one present and syntactically well-formed:

hreflanghrefLooks right?
en/en/Yes
en-uk/en-uk/Yes, at a glance
fr/fr/Yes
es/es/Yes

Running this exact set through this tool's own engine reports:

FindingSeverityVisible by reading the /en/ page alone?
"en-uk" is not a valid hreflang value — "UK" was never assigned an ISO 3166-1 region code (the real one is "GB").DangerNo — the tag is syntactically well-formed.
The /es/ page does not link back to /en/, so the annotation is not reciprocal.DangerNo — requires opening /es/ and comparing its own tags.
No x-default declared for 4 language variants.NoticeOnly if you happen to know to look for it.

2 danger-level findings, 1 notice, from a set that reads as complete on the page you'd actually be looking at. Google's hreflang documentation states that a non-reciprocal annotation may be ignored entirely for both pages in the pair, so the /en/–/es/ relationship in this example gets no hreflang benefit at all despite four tags that all look fine. Figures taken directly from this tool's own buildReport(), not invented.

Neither approach is wrong for what it does. A quick read catches an obviously broken tag; it was never going to catch a plausible-looking invalid code or a link-back on a page you didn't open. This tool checks the parts that require comparing every value against a reference table and every alternate against every other alternate — the part manual review runs out of attention for first.

Common questions

Why is checking hreflang tags by eye unreliable?

Because the two failures that matter most are invisible from reading one page's markup. An invalid region code like "en-uk" is syntactically well-formed and reads as correct — "UK" looks like the obvious abbreviation for the United Kingdom, but ISO 3166-1 never assigned it a country code; the real one is "GB". And reciprocity failures require opening every alternate page and checking that each one links back, which does not show up from reading the page you started on.

What does a manual review usually catch, and what does it miss?

A human reviewer catches an obviously malformed tag — a missing href, a typo in the attribute name — because those look wrong on sight. What gets missed is exactly what looks right: a region code that reads like it should be valid, and a link-back on a page nobody opened. Reciprocity in particular requires checking N-1 other pages for every page in the set, which nobody does by hand past two or three languages.

How big is the gap in practice?

In the worked example on this page, a 4-language hreflang set with every tag present and well-formed at a glance carries 2 danger-level findings once actually checked: an invalid "en-uk" code, and one alternate page that never links back, breaking reciprocity for that pair. Google's own documentation says a non-reciprocal annotation may be ignored entirely — meaning the page pair with the broken link-back gets no hreflang benefit at all, despite four tags that all looked fine.

Is reading the tags by hand ever enough?

For a two-page site where you personally maintain both pages and remember to update both at once, eyeballing them can work. It stops scaling the moment a third language, a redirect, or a second author enters the picture, because reciprocity is a claim about every other page in the set, not about the one you are looking at.

Does this tool fetch pages for me?

The Node CLI does, over real HTTP, which is why it can check reciprocity unattended in CI. The browser page cannot reliably fetch a different origin itself (cross-origin requests are usually blocked), so it checks reciprocity from HTML you paste in for each alternate — still mechanical, still exhaustive, just not automatic in the browser the way the CLI is.

Nothing here says or implies that checking hreflang tags by hand is a bad practice — it is the obvious first move, and it catches obviously broken tags immediately. This page describes the specific failures it structurally cannot catch, not a fault in anyone who does it.

Open hreflang Reciprocity Check and run it against your own pages.