List your language versions once and get the complete, reciprocal block for every page in the set, as HTML link tags, HTTP headers or XML sitemap markup. Already have tags and want them checked? Use the hreflang checker.
Everything runs in your browser. The URLs you type 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.
Generating the markup is the easy half. The half that goes wrong is deciding what belongs in the set before you generate anything, because an hreflang cluster is validated as a unit: one bad member and the whole set is discarded.
A set is a group of URLs that are the same page for different audiences. Not similar pages, not a category and its subcategory: the same page. Your German pricing page belongs in a set with your English pricing page, and in no other set. Every URL you list must be one you control and one that is indexable on its own.
Work page by page, not site-wide. A common failure is generating one set for the homepage and assuming the rest of the site inherits it. It does not. Each page in your site needs its own set, listing its own equivalents.
This is the part the generator above exists to get right. If you have three
language versions, each of the three pages carries all three tags, including the
one pointing at itself. Nine tags in total, not six, and the three blocks are
byte-for-byte identical apart from an optional
x-default.
People routinely generate "alternates" instead: two tags per page, each pointing at the other versions and omitting itself. That set never forms. The missing self-reference is why, and it is invisible unless you count the tags.
es is a better tag
than es-MX when you
publish one Spanish version for every Spanish-speaking audience, because it is the
accurate description of what you have. Adding a region narrows who the page is
offered to, so a region you cannot back with a genuinely different page costs you
reach for nothing.
Reach for a region only when the pages actually differ by country: different
prices, different stock, different legal text. And remember the region is a
country from ISO 3166-1 Alpha-2, which is why
en-UK and
es-419
both fail. The generator rejects them as you type; the reasoning is on the
hreflang rules
page.
x-default names the
page to serve a visitor no listed version fits: a language selector, or your
primary market's page doing double duty. It is optional, a set may contain at most
one, and two of them invalidate the set.
It is not a catch-all for "everyone else", and pointing it at a page that is already in the set under its own language tag is fine and common. What it must not be is a URL that appears nowhere else in the set.
The generator produces all three. They are equivalent to a search engine, so the choice is an engineering one. Pick one and use it consistently: implementing the same set two ways invites the two copies to drift, and a stale copy is worse than no copy.
A block of
<link rel="alternate">
elements in the
<head>. Use
this unless you have a reason not to: it is the form every tutorial and every
debugging tool assumes, and it is visible in view-source, which matters more than
it sounds when someone is trying to work out why a set broke.
The cost is weight. A set of twenty locales puts twenty lines in the head of every one of those twenty pages, on every page of the site. That is when the sitemap form starts to look attractive.
A PDF, an image or any other non-HTML resource has nowhere to put a link tag, so the annotation moves into the response headers instead. Same information, same rules, different transport. If you are localising documentation as PDFs, this is the only option available to you.
It is also the easiest form to get wrong operationally, because the headers live in server config rather than in the file, so they are invisible to anyone reviewing the content and easy to lose in a migration.
Moving hreflang into the sitemap changes the syntax, not the requirements. Each
<url> entry
needs an <xhtml:link>
for every page in the set, including itself, which means the same block repeated
under every URL. Reciprocity, self-reference, absolute URLs and the one-x-default
limit all still apply.
The one extra trap is the namespace: declare
xmlns:xhtml on the
<urlset>
element or the annotations are ignored wholesale, silently. The generator includes
it. The appeal of this form is that a large cluster stops bloating every page's
head and one file becomes the single place the set is defined. The cost is that the
tags are no longer visible in the page a developer is looking at.
For a two-language site:
<link rel="alternate" hreflang="en" href="https://example.com/" />
alongside
<link rel="alternate" hreflang="de" href="https://example.com/de/" />,
with both lines present on both pages. The commonest mistake in an
otherwise correct example is listing only the other page.
The number of pages in the set, squared, plus one x-default per page if you use one. Three languages means three tags on each of three pages: nine. Ten languages means a hundred. That growth is why large sites move the set into an XML sitemap, and why generating the blocks by hand stops being realistic somewhere around four or five locales.
Yes. Every page in a set must include a tag pointing at itself. Omitting the self-reference means the page is not recognised as a member of the set, and the set does not form. This is the single most common reason a set that looks right in view-source does nothing.
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 sets. All three are equivalent to a search engine. Pick one:
implementing the same set two ways invites the copies to drift apart.
No, it is optional. Add one when you have a genuine fallback for visitors no listed version fits, typically a language selector or your primary market's page. A set may contain at most one; two invalidate it. Pointing x-default at a page already in the set under its own language tag is fine.
No. The generator is JavaScript running on this page, and there is no server-side code here that could receive a URL. Nothing is uploaded, logged or fetched. You can disconnect from the network and it still works, which is the simplest way to satisfy yourself that the claim is true.