Skip to content

🌐 hreflang Tag Generator

Pick a base URL and target locales to generate <link rel="alternate" hreflang="..."> tags in bulk. Auto x-default, ISO 639 + country code validation, CSV bulk multi-path mode.

100% Free No signup Browser-only 5 languages Dark mode

🔒 About Privacy

Comma-separated. ISO 639-1 (en) or ISO 639-1 + ISO 3166-1 (en-US, zh-CN).

📄 Generated tags


📖 Where people get stuck

Generates <link rel="alternate" hreflang="…"> tags from a base URL and a set of locales, supporting path-prefix, subdomain and query-parameter URL patterns, automatic x-default, and bulk generation from CSV. Everything runs in the browser. hreflang is not a ranking factor — it merely declares that another language version of the same content exists here, and helps the search engine choose which one to show.

Case What happens What to do
The set is ignored because the references are not mutual hreflang only works when every page in the group points at every other — if the Japanese page points at the English one but the English one does not point back, Google discards the whole set. Easily missed is that each page also needs a hreflang tag pointing at itself: the Japanese page carries a hreflang="ja" entry for its own URL. The most common failure in practice is updating one page and forgetting the other languages — the operational burden that adding a new language means updating the tags on every existing page is the essential difficulty of this mechanism. Put the generated set of tags, unchanged, into the <head> of every language version — the content does not differ per page. As an implementation, looping over the language list in the page template is the only sane approach; writing them by hand always drifts somewhere. For a large site, consider declaring them in sitemap.xml rather than in HTML: <xhtml:link> expresses the same information, and having every page in one file makes a missed update structurally impossible. Verify in the International Targeting report in Search Console — a no return tags message there is exactly the missing mutual reference.
The language code is malformed The value is a language, or a language plus a region; a region alone is not validhreflang="US" means nothing. Common errors include en-UK, where the correct code is en-GB, using bare zh and expecting it to distinguish simplified from traditional, and an underscore, as in ja_JP, where a hyphen is required. Chinese deserves particular care: simplified and traditional are distinguished by script codes, zh-Hans and zh-Hant. zh-CN and zh-TW also work, but being regions they do not match simplified-script readers in Singapore or Malaysia. The language code is ISO 639-1, the region code is ISO 3166-1 alpha-2, and the separator is a hyphen. More importantly, ask whether you need the region at all: hreflang="en" matches every English speaker, whereas hreflang="en-US" matches only English speakers in the United States. If your content and prices do not vary by country, omitting the region reaches more people. Reserve regions for pages where currency, shipping or legal requirements genuinely differ. A free hreflang checker is the reliable way to verify, but start by putting the HTML of two language versions side by side and confirming with your own eyes that they point at each other — most errors are caught right there.
A conflicting canonical keeps the other version out of the index hreflang and canonical do different jobs and are easily conflated. Each language version must have a canonical pointing at itself. Set the canonical of /en/about/ to /ja/about/ and you have declared that the English page is a duplicate and only the Japanese one should be indexed — the English page disappears from search results. The hreflang set is then ignored as well, since a group whose canonicals point elsewhere is not trusted. It is the classic case of a well-intentioned setting — surely a multilingual site needs canonicalisation — producing exactly the opposite of what was wanted. Every language version must have a self-referencing canonical. hreflang says another language version exists; canonical says this page is itself the canonical one — they only work correctly together. Reserve canonical for duplicate URLs within the same language: with and without a trailing slash, URLs carrying tracking parameters, print views. Once implemented, check Google-selected canonical in the Search Console URL inspection — if it differs from your intent, that is hard evidence that your declaration is not getting through.

Adding hreflang does not guarantee that every language version gets indexed. A page with a thin translation, or with raw machine translation, fails on quality regardless of hreflang — this site retired its Portuguese and German versions in July 2026 for exactly that reason: they were duplicates of the English pages with no value of their own, and 576 URLs were consolidated with 301 redirects. Before adding a language, ask whether the content is worth reading in that language; adding a pile of thin translations is a liability for the site as a whole. There are three places to declare it — the HTML <link>, an HTTP response header, or sitemap.xml — and any one of them suffices; combining them is allowed, but contradictions cause the whole thing to be ignored. For files with no <head>, such as PDFs, use the HTTP Link: header.

📖 How to Use

  1. 1
    Enter base URL
    Enter the URL you want to localize.
  2. 2
    Specify locales
    Enter target language codes comma-separated (en, ja, ko).
  3. 3
    Get tags
    Paste the generated <link> tags into each page <head>.

❓ Frequently Asked Questions

Is x-default required?
Not required but recommended by Google. It is the fallback shown when no locale matches.
Where should hreflang go?
HTML , HTTP header, or XML sitemap. Sitemap is easier for large sites.
Are bidirectional links required?
Yes. A→B and B→A must both declare hreflang. Google ignores one-sided links. This tool emits full bidirectional sets.
🐛 Found a bug or issue with this tool?

Free to use, no signup. Even just the steps to reproduce are helpful. Reports go directly to the operator and help us fix issues.

* Browser info (UA / screen / language / URL) is sent automatically to help reproduce the issue