Skip to content

SEOPart of: International SEO

hreflang and canonical tags: how to use them together

hreflang and canonical tags do different jobs, and they only work together when every language version is canonical to itself. The rules, examples for tricky cases, and the conflicts that make Google ignore your hreflang.

getReport teamUpdated 26 Sept 202611 min read

hreflang and canonical tags answer two different questions. A canonical tag says "of these copies of the same content, index this URL". hreflang says "these pages are translations or regional versions of each other; show each searcher the right one". They work together when every language version has a canonical pointing at itself and the hreflang set lists exactly those canonical URLs. They fight when a canonical points from one language to another, because then Google treats the version as a duplicate and drops it, hreflang and all. This guide is for anyone running a site in more than one language or country; it is part of our guide to international SEO: site structure, hreflang and language targeting.

Quick answer

  • Every language or regional version is canonical to itself. /de/ has <link rel="canonical" href="https://example.com/de/">, not the English URL.
  • hreflang lists canonical URLs only. No parameters, no redirecting URLs, no URLs whose own canonical points elsewhere.
  • Never canonicalise across languages. A German page with an English canonical tells Google it is a copy; the German page drops out and its hreflang is ignored.
  • Duplicates within one language (tracking parameters, sort orders, print versions) canonicalise to the clean URL of the same language, and only that clean URL appears in hreflang.
  • Same-language regional pages (en-GB, en-US) can stay self-canonical; Google may fold them together, but hreflang still lets it show the local URL.
  • Check one page per template with the hreflang checker and each version's canonical with the canonical checker.

Canonical vs hreflang: what each tag does

Canonical taghreflang
Question it answersWhich URL of these duplicates should be indexed?Which version of this page suits which language or country?
Relationship between pagesCopies of the same contentEquivalents in different languages or regions
Effect on indexingNon-canonical copies are usually not indexedEvery version stays indexed on its own URL
DirectionOne-way: copy → preferred URLReciprocal: every version lists every version
Syntax<link rel="canonical" href="…"><link rel="alternate" hreflang="de" href="…">

Both are hints, not directives. Google weighs them with other signals: redirects, internal links, sitemaps and the content itself. When the two agree, the hints are strong. When they disagree, Google has to choose, and the canonical usually wins, because it is a statement about which URL deserves to exist in the index at all.

Our guide to canonical tags covers canonicals in general, and the complete hreflang reference covers syntax and placement. This page is about the overlap.

The rule: self-referencing canonical, same URLs in hreflang

Here is a correct pair of pages. The German version:

HTML
<!-- https://example-shop.hr/de/schuhe/ -->
<link rel="canonical" href="https://example-shop.hr/de/schuhe/">
<link rel="alternate" hreflang="hr" href="https://example-shop.hr/cipele/">
<link rel="alternate" hreflang="de" href="https://example-shop.hr/de/schuhe/">
<link rel="alternate" hreflang="en" href="https://example-shop.hr/en/shoes/">
<link rel="alternate" hreflang="x-default" href="https://example-shop.hr/en/shoes/">

The English version:

HTML
<!-- https://example-shop.hr/en/shoes/ -->
<link rel="canonical" href="https://example-shop.hr/en/shoes/">
<link rel="alternate" hreflang="hr" href="https://example-shop.hr/cipele/">
<link rel="alternate" hreflang="de" href="https://example-shop.hr/de/schuhe/">
<link rel="alternate" hreflang="en" href="https://example-shop.hr/en/shoes/">
<link rel="alternate" hreflang="x-default" href="https://example-shop.hr/en/shoes/">

Three things line up on every version:

  1. The canonical is the page's own URL.
  2. The hreflang entry for the page's own language is the same URL (the self-reference).
  3. Every other entry is the canonical URL of that version, character for character: same protocol, same host, same trailing slash.

Google's documentation on consolidating duplicate URLs puts it this way: when you use hreflang, specify a canonical page in the same language, or the best possible substitute language if a same-language canonical does not exist.

Why cross-language canonicals break hreflang

The most damaging mistake is pointing every version's canonical at the "main" language:

HTML
<!-- https://example-shop.hr/de/schuhe/  (wrong) -->
<link rel="canonical" href="https://example-shop.hr/en/shoes/">
<link rel="alternate" hreflang="de" href="https://example-shop.hr/de/schuhe/">

This page now sends two contradicting messages. The canonical says "I am a copy of the English page, index that instead". The hreflang says "I am the German version, show me to German speakers". Google tends to follow the canonical: the German URL is treated as a duplicate and not indexed, and an hreflang set that points at a non-indexed URL has nothing to show. German searchers get the English page.

It usually happens for one of three reasons:

  • A theme or plugin default sets the canonical from the "original" post in a translation plugin.
  • A misunderstanding of duplicate content. Translated pages are not duplicates. Google's localized versions documentation says language versions are only considered duplicates when the main content is left untranslated, for example when only the menu and footer change.
  • A migration shortcut, where new language folders were launched with canonicals to the old site "until they are ready" and never switched.

The fix is always the same: make each version self-canonical. If a version is genuinely not ready, keep it out of the hreflang set and out of the index with noindex, rather than canonicalising it to another language.

Tricky cases

Parameters and tracking codes

A URL such as /de/schuhe/?utm_source=newsletter should have a canonical pointing at /de/schuhe/, the clean German URL, and the hreflang tags on it should be identical to those on the clean URL. Never list parameter URLs in hreflang. The guide on query parameters and canonicals for tracking links covers the canonical side.

Paginated and filtered pages

Page 2 of a German category is its own page: its canonical points at itself (/de/schuhe/?page=2), and its hreflang points at page 2 of the other languages, not at their page 1. Filter URLs you keep out of the index (canonicalised to the unfiltered category or noindex) should carry no hreflang at all.

Same language, different countries

en-GB, en-US and en-AU pages often have nearly identical text. Keep them self-canonical anyway. Google may still decide they are duplicates and choose one as canonical, which Search Console's URL Inspection shows as "Google-selected canonical". With a correct hreflang set, Google can still show the local URL to searchers in each country, even when it indexes one of them. Local prices, currency, spelling, phone numbers and delivery terms make each version clearly its own and reduce the folding.

Separate domains and ccTLDs

The rules do not change across domains. example.de is canonical to itself, example.fr to itself, and each lists the others in hreflang. A cross-domain canonical from example.de to example.com removes the German site from the index just as it would within one domain.

Pages that exist in one language only

A blog post written only in English has no hreflang set, or a set with only itself and x-default. Do not point the German hreflang slot at the German home page and do not canonicalise anything to fill the gap. An empty cell is correct.

x-default and language pickers

The x-default URL follows the same rule: it must be canonical to itself and answer 200. A language picker at / that redirects visitors by IP address cannot be the x-default, because Googlebot follows the redirect. x-default explained covers the options.

How to check hreflang and canonicals together

Start with one page per template (home, category, product, article) in one language.

The checker reads the page's hreflang tags, validates the codes and the set, then fetches up to 50 alternates and confirms that each links back. It counts the self-reference as present when an entry matches either the page's own URL or its canonical, and it reports every alternate that redirects or does not answer 200. A redirecting alternate is often the first sign of a canonical mismatch: the hreflang lists /de/schuhe while the site redirects to /de/schuhe/.

Then check the canonical of each language version, since the hreflang checker does not compare the alternates' canonicals with the set:

For each version, the canonical should be the URL the hreflang set uses for it. Google Search Console's URL Inspection adds Google's side: "User-declared canonical" versus "Google-selected canonical". When Google picks another language's URL as canonical for your German page, the content is probably not translated enough, or the canonical points there.

Fixing a broken set, step by step

  1. List every version's canonical URL in a table, one row per page, one column per language. This table is the single source of truth.
  2. Set each page's canonical to its own cell. In WordPress, the SEO plugin writes it; the guide to multilingual WordPress with WPML and Polylang covers the integration settings.
  3. Generate hreflang from the same table. The hreflang generator turns it into tags, sitemap entries or HTTP headers, with every version listing itself.
  4. Remove parameter, redirecting and noindex URLs from every set.
  5. Deploy to all versions at once and re-run the checker on one page per template.
  6. Watch URL Inspection over the next weeks for the Google-selected canonical of each version.

Common mistakes

  • One canonical for all languages set in a shared layout or header template.
  • hreflang pointing at URLs that redirect, usually because of the trailing slash, http versus https, or www.
  • Canonical to the original language in a translation plugin after importing content.
  • noindex on a version that is still listed in hreflang, which sends mixed signals and wastes the slot.
  • Mixing methods: canonicals in the HTML say one URL, hreflang in the sitemap lists another form of it.
  • Canonicalising regional duplicates to one country (en-AU → en-US) instead of making them self-canonical and differentiating the content.

Questions people ask

Can a page have both a canonical and hreflang tags?

Yes, and it should. Each version carries one canonical pointing at itself and a set of hreflang tags listing every version, itself included. The two do different jobs: the canonical consolidates duplicates of the same page in one language, and hreflang connects the versions in different languages. Problems only appear when the canonical points at a different version than the page itself.

Should all language versions canonicalise to the main language?

No. A canonical from the German page to the English page tells Google the German page is a duplicate, so Google stops indexing it and ignores its hreflang. Translations are not duplicate content. Give each language version a self-referencing canonical, and use hreflang to tell Google which version belongs to which language or country.

Why does Google pick another language version as canonical?

Usually because the pages look like copies. If the main content is untranslated, or only prices and the menu change, Google may treat the versions as duplicates and pick one. A cross-language canonical tag, internal links pointing mostly at one version, or hreflang pointing at redirecting URLs can cause the same result. Translate the main content, make each version self-canonical and fix the hreflang URLs.

Do hreflang URLs have to match the canonical URL exactly?

Yes, in practice. Each URL in the set should be the canonical URL of that version, with the same protocol, host, path and trailing slash. An hreflang entry that points at a URL which redirects, or whose own canonical points elsewhere, is a non-canonical URL, and Google can ignore that entry or the whole pair. Copy the URLs from one table rather than typing them.

Check your site before and after Check