Generate a complete, reciprocal hreflang set for every page in your language cluster, or paste the tags you already have and find out precisely which rule they break, and how to fix it.
Everything runs in your browser. The URLs you paste are never sent to a server, because there is no server-side code to receive them. No account, no crawling, no saved projects.
One row per language version. Each row needs an hreflang value and the absolute URL of that version.
The page to serve when no language version matches the user. Usually a language selector, or your primary market's page. At most one per set.
pages × tags. Every page gets the same complete block, including a tag pointing at itself.
Paste each block into the <head>
of the page named above it.
For non-HTML resources such as PDFs, where there is no
<head> to hold a link tag.
Send the header named below with the response for that URL.
One <url> entry per page,
each repeating the whole set. Use this or the link tags, not both.
/
Paste link tags, a whole <head>,
an HTTP Link: header, or sitemap
<xhtml:link> entries.
--- https://example.com/ ---
<link rel="alternate" hreflang="en" href="https://example.com/">
<link rel="alternate" hreflang="de" href="https://example.com/de/">
--- https://example.com/de/ ---
<link rel="alternate" hreflang="en" href="https://example.com/">
<link rel="alternate" hreflang="de" href="https://example.com/de/">
Reciprocity can only be proven by seeing both ends. This tool never fetches a URL, so it asks you for the other end rather than crawling for it.
errors, warnings, notes across .
No problems found. Every rule this tool can check passes.
An hreflang set either works completely or not at all. Google validates the cluster as a unit, and a single malformed tag can cause the whole set to be discarded silently, with nothing in the HTML to show for it. These are the rules the validator above checks, in roughly the order people get them wrong.
An hreflang value is a language, optionally followed by a region:
en,
en-GB,
pt-BR.
The language comes from ISO 639-1 (two letters) and the region from ISO 3166-1
Alpha-2 (two letters). These are separate registries, and codes from neighbouring
standards are not interchangeable, however sensible they look.
The region is a country, never a language. So
en-EN
is meaningless, and
en-GB
is what you meant. A region on its own is not a valid hreflang value either: you may
write en without a
region, but never a region without a language.
en-UK is invalid. It is
en-GB
This is the single most common hreflang error in the wild. The United Kingdom's ISO 3166-1 Alpha-2 code is GB. "UK" is only exceptionally reserved in the standard: reserved so nobody else can use it, but not assigned for general use. Google ignores the tag.
It slips through because .uk
is the country's top-level domain, and because "UK" is what everybody calls the place.
Neither fact has any bearing on ISO 3166-1. The same trap catches
en-EU: there
is no ISO country code for the European Union, so there is no valid way to target it.
Target a language, or target individual countries.
en_US is
invalid; en-US
is correct. Underscores are the Java and POSIX locale convention, and they leak into
markup whenever an hreflang value is generated from an application's internal locale
identifier. hreflang follows BCP 47, which uses hyphens. If your CMS stores locales as
en_US, convert on output.
es-419 is invalid for hreflang
419 is the UN M.49 code
for Latin America. M.49 is a statistical registry that describes groups of
countries, and it is a perfectly good way to label content. It is not ISO 3166-1
Alpha-2, it has no Alpha-2 equivalent, and strict hreflang validators reject it.
This one is genuinely confusing because es-419
is valid BCP 47: BCP 47 permits M.49 region subtags. hreflang is narrower than
BCP 47. Being able to point at the spec that blesses your tag is not the same as the
tag working.
The fix is almost always to drop the region and use plain
es. If you
publish one neutral Latin-American Spanish version for every Spanish-speaking audience,
then es is not a compromise;
it is the accurate description of what you have. Reach for
es-MX or
es-AR only when you
genuinely serve a different page per country.
We hit this exact problem on a sister site. Its Spanish content is neutral
Latin-American Spanish, and the obvious tag looked like
es-419.
The decision recorded in its codebase was to tag it
es: there is a
single Spanish version for all Spanish audiences, and 419 is an M.49 region code that
strict validators reject. That comment is still at the top of the file, with a note
telling the next person not to change it back.
A page's hreflang block lists every page in the set, including the page the block is on. A set of three languages means three tags in each of three blocks, not two "alternates" per page. Without a self-reference, Google does not recognise the page as a member of the set, and the set does not form.
This is why the generator above emits a complete block per page rather than one block
for the set. It is also why the blocks are identical to each other: a correct hreflang
cluster is the same list of tags repeated on every page, plus at most one
x-default.
If page A declares page B as an alternate, page B must declare page A. Google treats the return link as confirmation that both pages agree they are versions of one another. Without it, anyone could claim an association with a page they do not control. One-way references are discarded.
Reciprocity is the rule that breaks most often after launch, because it breaks remotely. Nothing changes on page A; someone edits page B, and A's tags stop working. Search Console reports it as "no return tags", which names the symptom rather than the page that caused it.
Verifying it means seeing both ends. The validator above will do it, but only for pages you paste in: it makes no network requests, so it cannot go and look at page B on your behalf. Anything it cannot see, it reports as unverified rather than assuming it is fine.
href="/de/"
is ignored. hreflang needs the full URL, scheme and hostname included:
href="https://example.com/de/".
Protocol-relative URLs (//example.com/de/)
are not good enough either, and they break outright once the tag is copied into a
sitemap or an HTTP header, where there is no document base URL to resolve against.
Match the URLs exactly, too. Return links are compared as strings.
http:// versus
https://, or a trailing
slash on one side and not the other, reads as a missing return tag even though both
URLs resolve to the same page in a browser.
x-default names the page
to serve when no language version matches the user, typically a language
selector or your primary market's page. It is optional. A set may have exactly zero or
one, and two of them invalidate the set.
Between the language and the region you may add a four-letter ISO 15924 script subtag.
The case that matters in practice is Chinese:
zh-Hans
for Simplified and
zh-Hant
for Traditional, optionally with a region:
zh-Hant-HK.
Use one only when the script genuinely distinguishes your content. Nothing else may
follow the region. BCP 47 variant subtags such as
en-GB-oxford
are well-formed but are not honoured by hreflang.
Within one set, a given language/region maps to exactly one URL. Declaring
hreflang="en" twice with
different targets gives Google no way to choose, so it discards the conflicting
values, often the whole set. The reverse is legal: two language codes may point at one
URL, if one page really does serve both audiences.
An hreflang set can be flawless and still do nothing, because two other directives overrule it. Both failures are invisible in the hreflang markup itself, which is why they survive review for months.
A page that canonicalises elsewhere. hreflang applies to canonical
pages. If your German page carries
<link rel="canonical" href="https://example.com/">,
you have told Google that the German page is a duplicate of the English one and should
not be indexed separately. Its hreflang tags are dropped. And here is the part that
catches people: the return link it owed the English page vanishes too. The
English page's tags now fail as well, on a page you never touched. Every page in an
hreflang set must be its own canonical.
A page that is noindex. Same shape, blunter cause.
noindex and hreflang are
contradictory instructions: hreflang asks Google to swap this page in for the right
audience, and noindex removes it from the index entirely. Noindex wins. The page leaves
the cluster and takes its return links with it.
Both are worth understanding rather than merely flagging, because the page reported as
broken is never the page that broke. Paste the full
<head> of each page
into the validator, canonical and robots tags included, and it will name
the page that actually caused the failure.
en-UK ever valid?
No. The United Kingdom's ISO 3166-1 Alpha-2 code is GB, so the correct value is
en-GB. "UK" is
exceptionally reserved in ISO 3166-1, not assigned, and Google ignores hreflang
values built from it. The .uk
domain does not change this.
es-419 rejected when it is valid BCP 47?
Because hreflang accepts a narrower set of tags than BCP 47 does. BCP 47 permits
UN M.49 numeric region subtags such as 419 (Latin America); hreflang requires the
region to be an ISO 3166-1 Alpha-2 country code, and 419 has no Alpha-2 equivalent
because it is a group of countries rather than one. Use plain
es for a single
Spanish version, or per-country codes such as
es-MX when you
genuinely serve different pages.
Yes. Every page in a set must include a tag pointing at itself. A set of three languages means each of the three pages carries all three tags. Omitting the self-reference means the page is not recognised as part of the set.
Usually one of three things. The two URLs do not match as strings: a trailing slash, or http against https. The target page canonicalises somewhere else, so its tags are discarded along with the return link it owed you. Or the target page is noindex. The page Search Console names is rarely the page that needs editing.
Pick one and use it consistently. Link tags in the
<head> are the
default for HTML pages. HTTP Link:
headers exist for non-HTML resources such as PDFs, which have no head to put tags
in. The XML sitemap form keeps the markup out of your pages entirely, which suits
very large sites. Implementing the same set two ways invites the two copies to
drift apart.
No. hreflang tells Google which version to show once it has decided your site is the right answer. It is a swap, not a ranking boost, and it will not rescue a page that does not rank on its own merits. It also does not resolve duplicate content: that is what canonical tags are for.
No. The generator and the validator are JavaScript running on this page. Nothing is uploaded, logged, or fetched. There is no server-side code here that could receive it, and the validator asks you to paste each page in a set rather than crawling for them precisely so that it never needs to make a request. You can disconnect from the network and both tools still work.