You translated your blog into three languages, added hreflang tags, and Google is still serving the English page to users in Madrid. This is the most common outcome of a first hreflang attempt, and it almost never means the concept is wrong. It means one of the strict rules got broken: a missing self-reference, a return tag that does not return, a language code in the wrong format, or a canonical tag quietly contradicting the whole set. Hreflang is unforgiving that way. Get every rule right and it works silently; miss one and Google discards the annotation and guesses. This guide shows you the three implementation methods, real code you can adapt, and the exact mistakes that break them.
Quick takeaways
- There are three ways to implement hreflang: HTML
<head>tags, XML sitemap entries, and HTTP headers. Pick one; do not mix methods for the same pages. - Every page must carry a self-referencing hreflang tag plus a tag for each alternate, and all of them must be reciprocal.
- Use ISO 639-1 language codes (
en,de) and optional ISO 3166-1 region codes in uppercase (en-US,en-GB). - Always add an
x-defaulttag as the fallback for users no other tag matches. - A CMS that emits hreflang server-side removes the two errors that break most setups: missing return tags and format mistakes.
What hreflang actually does
Hreflang is an annotation that tells search engines which language and, optionally, which region a page is meant for. When you have the same content in English, German, and Spanish, hreflang says: "serve the English one to English speakers, the German one to German speakers, and here is the fallback for everyone else." Without it, Google has to infer the relationship between your pages, and when it infers wrong it either serves the wrong language or collapses your variants into suspected duplicates.
One clarification that saves hours of debugging: the HTML lang attribute and hreflang are not the same thing. The lang attribute declares the language of the page you are on. Hreflang declares the language of the alternate URLs you are pointing to. You need both, and they do different jobs.
The three implementation methods
Google treats all three methods as equivalent. Choose the one that fits how your site is built, and use only that one for a given set of pages.
| Method | Where it lives | Best for | Watch out for |
|---|---|---|---|
HTML <head> tags |
In the <head> of every page |
Most sites, easiest to add | Adds markup to every page's head |
| XML sitemap | In your sitemap file | Large multilingual sites | Harder to author and audit by hand |
| HTTP headers | In the server response | Non-HTML files like PDFs | Requires server-level configuration |
Method 1: HTML head tags
This is the most common method. In the <head> of every language version, you list a <link> element for each version of that page, including the page itself. Here is a complete set for a post that exists in English, German, and Spanish:
<link rel="alternate" hreflang="en" href="https://example.com/blog/post" />
<link rel="alternate" hreflang="de" href="https://example.com/de/blog/post" />
<link rel="alternate" hreflang="es" href="https://example.com/es/blog/post" />
<link rel="alternate" hreflang="x-default" href="https://example.com/blog/post" />
The critical detail: this exact block, pointing to all four URLs, must appear on the English page, the German page, and the Spanish page. Not a version that only lists "the others." Every page lists every version, itself included.
Method 2: XML sitemap
For large sites, putting hreflang in the <head> of thousands of pages bloats every response. The sitemap method moves the annotations into your sitemap file using xhtml:link entries. Each URL entry lists all its alternates:
<url>
<loc>https://example.com/blog/post</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/blog/post" />
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/post" />
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/blog/post" />
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/blog/post" />
</url>
You repeat a parallel <url> block for the German and Spanish URLs. It keeps your HTML clean, but it is tedious to author and audit manually, which is why sitemap hreflang is usually generated by the CMS rather than hand-written.
Method 3: HTTP headers
For files that are not HTML, PDFs, for example, you cannot add a <head> tag, so you declare hreflang in the HTTP response header:
Link: <https://example.com/doc.pdf>; rel="alternate"; hreflang="en",
<https://example.com/de/doc.pdf>; rel="alternate"; hreflang="de"
Most blogs never need this method. It exists for completeness and for document-heavy sites.
The rules that make or break hreflang
Method matters less than getting these five rules exactly right. Break one and the entire annotation set for those pages gets ignored.
1. Self-referencing tags are mandatory. Every page must include an hreflang tag pointing to itself. A German page that lists English and Spanish but not German is invalid.
2. Return tags must be reciprocal. If page A points to page B as an alternate, page B must point back to page A. If your /us/ page links to /uk/, the /uk/ page must link back to /us/. One-directional annotations are discarded.
3. Use the correct code format. Language codes are lowercase ISO 639-1 (en, de, fr). Region codes are uppercase ISO 3166-1 Alpha 2 (US, GB), joined with a hyphen: en-US, en-GB. en_US with an underscore is wrong. A bare region without a language (hreflang="US") is also wrong; hreflang targets language first.
4. Always include x-default. The x-default value is the fallback for users whose language and region none of your other tags match. Leave it out and those users get an arbitrary version.
5. Hreflang and canonical must agree. Each language version should canonicalize to itself. A German page that canonicalizes to the English version while also claiming to be the German alternate sends contradictory signals and can get your translated pages dropped from the index.
The mistakes that silently cancel your work
These are the failure modes that produce "I added hreflang and nothing changed":

- Missing return tags. The number-one cause. An audit tool like Google Search Console's International Targeting report or a site crawler will flag these.
- Wrong or absent canonicals. A translated page canonicalizing back to English tells Google the translation is a duplicate, not an alternate.
- Mixing methods. Some pages annotated in the head, others in the sitemap, with conflicting sets. Pick one method per page set.
- Codes that do not exist. Typos like
hreflang="eng"or invented region codes are ignored. - Absolute vs. relative URL mistakes. Use full absolute URLs (
https://...) in hreflang, always.
The common thread is that these are all mechanical, and all invisible until you audit. That is precisely why hand-maintaining hreflang across a growing multilingual blog is fragile: every new post is another chance to break reciprocity.
Let the CMS emit hreflang for you
Every rule above is deterministic. There is nothing creative about a self-referencing, reciprocal, correctly-formatted hreflang set; it is bookkeeping that a machine should do. That is the design choice behind Kamaan.
Kamaan is a headless blog CMS with Auto-Multilingual Delivery: you publish an English post once, it produces the language versions, and it emits the hreflang tags server-side, self-referencing and reciprocal, in the correct format, with x-default included. You never hand-write a <link rel="alternate"> tag, and you never ship a post with a missing return tag, because the tags are generated from the set of versions rather than typed per page. It uses the subfolder pattern (/de/blog, /es/blog) that this guide recommends, and it delivers content through a plain REST API into Next.js, Nuxt, SvelteKit, Astro, React, or Vue, so the hreflang lands correctly in whatever framework renders your blog.
If you run more than one blog, that correctness compounds: Kamaan's Multi-Site Management means every site you operate gets the same server-side hreflang from a single dashboard, instead of you re-verifying tags site by site.
Real-world scenarios
A developer maintaining a Next.js marketing blog kept getting "no return tags" errors in Search Console every time the content team added a language. They moved the blog to Kamaan on the Growth plan ($49/mo, 3 sites, 3 languages). Now every post ships with reciprocal hreflang emitted server-side; the developer fetches content through Kamaan's REST API with a normal fetch() and renders it, and the return-tag errors are gone because the tags are generated from the version set, not typed by hand.
A multi-product founder runs three SaaS blogs, each in four languages. Auditing twelve hreflang matrices by hand was a recurring chore. On Kamaan's Scale plan ($99/mo, 10 sites, 10 languages), all three blogs emit correct hreflang from one account, and adding a fourth product blog inherits the same correctness with no extra setup.
FAQ
What is hreflang implementation?
Hreflang implementation is the process of adding hreflang annotations to your pages so search engines serve the right language and region version to each user. It can be done through HTML head tags, XML sitemap entries, or HTTP headers, and it requires self-referencing, reciprocal tags in the correct code format.
What is an example of a hreflang tag?
A basic HTML example is <link rel="alternate" hreflang="de" href="https://example.com/de/blog/post" />, which tells search engines the German version of that post lives at that URL. Every language version, plus an x-default, needs its own line, and the full set must appear on every version of the page.
Do I need a self-referencing hreflang tag?
Yes. Every page must include an hreflang tag pointing to itself, in addition to tags for each alternate. Without the self-reference, the annotation set is invalid and search engines may ignore it.
What is x-default in hreflang?
x-default is the fallback value. It tells search engines which URL to serve to users whose language and region are not matched by any of your specific hreflang tags. Every set should include one.
Which hreflang method is best: HTML, sitemap, or HTTP headers?
Google treats all three as equivalent. HTML head tags are easiest for most blogs, the XML sitemap method suits very large sites because it avoids bloating every page's head, and HTTP headers are only needed for non-HTML files like PDFs. Use one method per page set, not a mix.
Why is my hreflang not working?
The most common causes are missing return tags (page A points to B but B does not point back), a canonical tag that contradicts hreflang, wrong code formats, or a missing self-reference. Google Search Console's International Targeting report and a site crawler will surface most of these errors.
Can a CMS handle hreflang automatically?
Yes. Because correct hreflang is fully deterministic, a headless CMS like Kamaan can emit the tags server-side from the set of published language versions, guaranteeing self-referencing, reciprocal, correctly-formatted annotations without anyone writing them by hand.
Related on Kamaan
- How to Build a Multilingual Blog. The full guide this implementation walkthrough sits under.
- Hreflang Tags Explained. The conceptual companion, covering what hreflang is and when you need it.
- Multilingual SEO. How hreflang fits alongside URL structure and localized content.
- Blog URL Structure for Multiple Languages. Choosing the subfolder pattern your hreflang tags will reference.
Start building with Kamaan
Stop hand-maintaining hreflang tags that break every time you add a language.
Kamaan gives you one dashboard for all your product blogs, auto-translated into every language your plan covers on each publish (up to 99+ on the Unlimited tier), with reciprocal hreflang emitted correctly server-side so your return-tag errors disappear. First month free.
