hreflang generator
List your versions and it writes the block. The part that matters is where the block goes: on every page in the set, unchanged, including each page’s own entry.
That is the rule sets actually fail on, and it is not visible in the markup any generator hands you.
It runs entirely in your browser. Nothing you paste is sent to us, stored, or logged.
Comma or tab separated, so a column pasted from a spreadsheet works. Use x-default for the page somebody sees when they match none of the others.
The block, for every page in the set
<link rel="alternate" hreflang="en-GB" href="https://example.com/en-gb/" /> <link rel="alternate" hreflang="en-US" href="https://example.com/en-us/" /> <link rel="alternate" hreflang="de" href="https://example.com/de/" /> <link rel="alternate" hreflang="x-default" href="https://example.com/" />
This same block goes on every page listed above, unchanged, including each page’s own entry. A version that names the others while none of them name it back is not processed at all.
What is wrong with the set
Nothing found. Every entry is a full URL, every code is the right shape, and no language or address is repeated.
Why the block goes everywhere
Each language version must list itself as well as all other language versions.
The middle three words are the ones people drop. A version that names its siblings while none of them names it back is not half-processed; the pairing is what Google reads, and a one-sided annotation gives it nothing to pair.
Search Console used to report that as “no return tags”, in a report Google has since retired. The requirement outlived the screen, so a broken set now fails quietly instead of loudly.
What it checks, and what it cannot
Four things about the set, none of which is visible in a block of markup:
- A language listed twice. Google reads one of them and there is no rule saying which, so the set becomes a coin toss you cannot see.
- One address under two languages. A single URL cannot be two versions, and the pair is discarded rather than half-applied.
- A relative path. hreflang needs a fully-qualified URL, and a relative one is ignored rather than resolved against the page.
- A code that is not the shape of a code. Checked by shape rather than against a closed list, because rejecting a correct input sends you hunting a bug that is ours.
It checks SHAPE, not existence. Whether those addresses resolve, or serve the language they claim, is not knowable from a text box, and finding out would mean fetching arbitrary URLs from a public endpoint. This site does not do that, for the same reason its crawler resolves every hop through a guard.
A missing x-default is reported as worth knowing rather than as an error, because Google calls it recommended. Reporting a recommendation as a fault is how a working set gets rebuilt for nothing.