Generate correct hreflang tags as HTML, an XML sitemap block, or an HTTP header, then paste your existing tags to catch missing x-default and return-tag errors. Everything runs in your browser, nothing is uploaded.
<link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" /> <link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" /> <link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" /> <link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" /> <link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" /> <link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
Paste these inside the <head> of every language version of the page. Each version must list the full set, including a self-referencing tag.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/blog/my-post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" />
<xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
</url>
<url>
<loc>https://example.com/es/blog/my-post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" />
<xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
</url>
<url>
<loc>https://example.com/de/blog/my-post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" />
<xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
</url>
<url>
<loc>https://example.com/fr/blog/my-post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" />
<xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
</url>
<url>
<loc>https://example.com/it/blog/my-post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/my-post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/my-post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/my-post" />
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/blog/my-post" />
<xhtml:link rel="alternate" hreflang="it" href="https://example.com/it/blog/my-post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/my-post" />
</url>
</urlset>Prefer keeping hreflang out of your <head>? Put it in your sitemap instead. Never do both for the same set.
Link: <https://example.com/blog/my-post>; rel="alternate"; hreflang="en", <https://example.com/es/blog/my-post>; rel="alternate"; hreflang="es", <https://example.com/de/blog/my-post>; rel="alternate"; hreflang="de", <https://example.com/fr/blog/my-post>; rel="alternate"; hreflang="fr", <https://example.com/it/blog/my-post>; rel="alternate"; hreflang="it", <https://example.com/blog/my-post>; rel="alternate"; hreflang="x-default"
Use this when the page is not HTML, for example a PDF, or when you set hreflang at the CDN / edge instead of in markup.
Hreflang is an HTML attribute that tells search engines a page has equivalent versions in other languages or regions. When someone in Mexico searches for your product, hreflang is how Google knows to show your Spanish page instead of your English one, without treating the two as duplicate content competing against each other.
The concept is simple. The execution is where most sites fail. hreflang has to be reciprocal: if your English page points at the Spanish version, the Spanish page must point back. It has to be self-referencing: every page lists itself in the set. And it has to use absolute URLs: relative paths are silently ignored. Miss any one of these on any one page and search engines throw out the entire cluster.
If you run a Next.js, Astro, or Nuxt blog on a headless CMS, generating the tags once is the easy part, this tool does it in seconds. The hard part is keeping the reciprocal set correct across dozens of posts and languages as you publish, translate, and re-slug over months. Every new translation you ship silently invalidates the return tags on all of its siblings until you go back and update them.
That is the exact busywork Kamaan is built to delete. You publish a post once. Kamaan auto-translates it into every language on your plan, serves each at its own URL, and emits the complete reciprocal hreflang set plus a correct per-language canonical, every time, with no manual step. The tool above shows you what correct output looks like. Kamaan makes correct output the default, at scale, across every product blog you run.
You need hreflang when the same page exists in more than one language or regional variant and you want Google to serve the right version to the right searcher. If you only publish in one language, you do not need hreflang at all. It is specifically for multilingual and multi-regional sites.
Yes. Every page in the cluster must list the full set of alternates, including a tag that points at itself. A Spanish page that lists English, German, and French but forgets to list its own Spanish URL is invalid, and Google will likely ignore the whole cluster.
x-default tells Google which URL to fall back to when none of your listed languages match the searcher. It usually points at your default or language-selector page. It is recommended, not strictly required, but adding it removes ambiguity for locales you do not explicitly target.
A canonical tag says "this is the single authoritative URL for this content." Hreflang says "these URLs are the same content in different languages, rank each one in its market." They work together: each language version should have a self-referencing canonical AND the full hreflang set. Never point a canonical from your Spanish page to your English page, or you will de-index the Spanish version.
Use language-only (like es) when the content targets all speakers of that language. Use language-region (like es-MX or en-GB) only when you have genuinely different content per country, for example different pricing or spelling. Do not add a region code you do not actually differentiate, it just creates more return-tag surface to get wrong.
Pick one, not several, for the same URL set. HTML head tags are simplest for most blogs. The XML sitemap variant keeps your markup clean and scales to thousands of URLs. The HTTP Link header is for non-HTML files like PDFs. This tool outputs all three so you can choose.
That error means page A points at page B as an alternate, but page B does not point back at page A. hreflang must be bidirectional across every page in the cluster. The most common cause is publishing the source language first and forgetting to add the source URL to the translated pages once they go live.
In a headless setup the tags are emitted by your frontend from data your CMS returns. The failure mode is manual: you translate a post, ship it at a new URL, and forget to update the return tags on every sibling. Kamaan removes that step entirely, it emits the full reciprocal hreflang set and a per-language canonical automatically every time you publish.