A company builds a European site, translates it into German, French and Spanish, and publishes all of it. Weeks later the German version cannot be found by anyone in Germany, while the English version appears there instead. Nothing is broken: the pages load, the text is correct, the links work. The cause is usually a few lines in the header of each page that nobody on the team reads, and that the engine reads very carefully. The problem is common on sites built by teams who have never had to run several language versions of the same page.
What hreflang does, and what it does not
Hreflang is an annotation placed in the page header, in the sitemap or in the HTTP response. For a given page it lists the equivalent pages in the other languages and for the other countries, each with a code: a language, optionally followed by a country. When someone in Germany searches in German, the engine may replace the English result with the German one, because the annotation tells it they are the same page for different audiences. It is a signal and not an order. It does not translate, does not give a page a better position, and does not stop a page from being shown elsewhere. All it does is choose the right version among pages that already exist and are already indexed.
Why can a page disappear because of it? Because the engine avoids showing several results that look identical. When it finds pages that are nearly the same, it groups them and keeps one, called the canonical. A German page with an untranslated menu, English product names and technical specifications that are identical to the English page looks, to the engine, very close to it. The hreflang annotation is what proves they are intended for different audiences. If the annotation is invalid, the proof is ignored. The engine keeps the English page, the German one is filed as a duplicate, and nobody is told.
The three classic mistakes
- No return link. The English page lists the German page as its equivalent, but the German page does not list the English one back. Hreflang only counts when both sides confirm each other, so the engine discards the pair. Every version has to list all the others and itself, and a version added later has to be added on every existing page, not only on the new one.
- Codes that do not exist. The language is written first, then an optional country, with the codes of the international standards. Typical errors: a country alone with no language, the United Kingdom written with the wrong letters, an underscore where a hyphen is expected, or a code invented by the translation plugin. An invalid code is ignored without any message, as if the line were not there.
- Annotations that contradict the canonical. The German page declares itself the German version, but its canonical tag points to the English page. The two signals say opposite things, and the canonical wins: the German page is told it is merely a copy. The same happens when hreflang points to addresses that redirect elsewhere, return an error or are blocked from indexing.
Other ways it goes wrong
Three more cases deserve a mention. The first is a page that does not reference itself: each page should appear in its own list, and many generators leave it out. The second is the default version. A special value, x-default, names the page to show when no language matches, which is often the English one or a language selector; omitting it is not an error but it leaves the choice to the engine. The third comes from automatic redirects by browser language or by IP address. If a visitor, or the engine's crawler, is sent to the English page whenever it arrives from outside a country, the crawler never sees the German page at all. Offer the choice with a visible link rather than forcing it.
How to see it from outside
- Open the German page and view its source. Look in the header for the lines that start with the word alternate and carry the hreflang attribute. Check that the German page itself is listed, that every language of your site is there, and that the codes are spelled as expected.
- Do the same on the English page and confirm that it lists the German one. Then follow each address listed and check that it loads directly, with no redirect, and carries the same list in return.
- Look at the canonical tag on each version. On a German page it should point to that same German page, not to the English one.
- In the free search tool for site owners, inspect the German address. If the report says the page is a duplicate and that the engine chose another canonical than yours, you have found the cause.
- In the performance report, filter on Germany and on the German page. If the English address collects the impressions that should belong to the German one, the substitution is not working.
| What you see | Likely cause | First thing to check |
|---|---|---|
| English page shows in German results | Annotation ignored; pages grouped as duplicates | Return links on both versions |
| German page reported as a duplicate | Canonical tag points to English | Canonical on the German page |
| Annotations present but no effect | Invalid codes or redirecting addresses | Spelling of codes; direct loading of each address |
| German version never crawled | Automatic redirect by language or country | Visit the German address without a redirect |
Fixing it in a sensible order
Start with the canonical tags, because a wrong canonical cancels everything else. Then the list itself: have it produced by one template or one plugin that you understand, rather than typed on each page, since manual lists drift apart as soon as a page is added. Generate the full set of pairs from a single source, including every page, not only the home page. Then remove the automatic redirect by language. Finally, resubmit the sitemap and wait: the engine has to recrawl every page of the group before it trusts the new signal, and how long that takes depends on the size of the site and how often it is visited. A site that is only partly translated should link only the pages that genuinely exist in each language.
Hreflang does not repair a poor translation. If the German page is a machine translation with an English menu, the engine still sees little difference from the English page, and the buyer who lands on it sees the same. Fix the markup first, because it costs a few hours, then decide honestly whether the page deserves to be shown to a German buyer. A page you would not forward to a customer should not be the one you ask the engine to prefer.