The "Canonical Wars": When hreflang and Canonical Tags Fight (and How to Stop Them)

Two tags walk into a <head>. One of them tells Google: "index this URL, it's the authoritative version." The other says: "I have equivalents for other languages, here are the URLs."

Played right, they cooperate. Played wrong, they cancel each other out and Google quietly drops your translated pages from the index entirely.

This is one of the most preventable, most common, and most invisible mistakes in international SEO. Let's untangle it.

What each tag is for

Canonical

Says "this is the official URL for this page's content." Used for duplicate-content consolidation — the same product listed at three URLs because of tracking parameters, or a printer-friendly version, or a paginated archive. One canonical, one indexed version.

hreflang

Says "this content exists in multiple languages or regions. Here are the URLs." Not duplicates — equivalents. Google indexes each one separately and shows the right one to the right audience.

Already you can see the conflict. Canonical says "collapse these." hreflang says "keep these separate." If both tags point at the same set of URLs, Google has to pick a side. It picks canonical. Every time.

The #1 way this goes wrong

Your Spanish page looks like this:

<link rel="canonical" href="https://example.com/" /> <link rel="alternate" hreflang="en" href="https://example.com/" /> <link rel="alternate" hreflang="es" href="https://example.com/es/" />

Read the canonical. It points at the English page. You just told Google "the Spanish page is not the real version — the English one is." Google obliges. It indexes the English URL and drops the Spanish one.

This almost always happens because a CMS or SEO plugin is generating canonical tags automatically, and the developer didn't notice that the rule was "point canonical at the default URL" rather than "point canonical at this page."

I've seen this in the wild on WordPress (Yoast with certain multilingual plugin configs), Shopify (when a third-party translate app mis-handles storefront_lang), and roll-your-own Django sites.

The rule

Every page's canonical points at itself. The English page canonicals to the English URL. The Spanish page canonicals to the Spanish URL. The French page canonicals to the French URL.

Spanish page, done right:

<link rel="canonical" href="https://example.com/es/" /> <link rel="alternate" hreflang="en" href="https://example.com/" /> <link rel="alternate" hreflang="es" href="https://example.com/es/" /> <link rel="alternate" hreflang="x-default" href="https://example.com/" />

Now hreflang and canonical agree. The page declares itself authoritative and says "my English sibling lives over there."

Edge cases worth knowing

Parameterized URLs on multilingual pages

If /es/?utm_source=newsletter should canonicalize to /es/, that's fine. Canonical to the clean version of the same language. Never canonical across languages.

Regional duplicates (es-MX vs es-ES)

Sometimes the content is literally identical between es-MX and es-ES — same Spanish, no regional differences. Don't use canonical to collapse them. Use hreflang to serve the right one and let Google index both. If the content is truly identical, Google's own deduplication will handle it without you forcing it.

Partially-translated pages

You launched the Spanish version with 80% of the content translated; the remaining 20% is still English. Don't canonical the Spanish page to the English one to "be safe." The Spanish page, even partially translated, is serving a different audience. Keep the self-canonical.

Machine translation plus noindex

Some teams noindex machine-translated pages to "avoid thin content." Don't. If you're going to translate a page, translate it with quality worth indexing. If you're not confident enough to index it, either improve it or don't publish it. Noindex on a page that's also declared in an hreflang cluster just breaks the cluster.

How to audit your own site

Run any crawler (Screaming Frog, Sitebulb, Ahrefs Site Audit). Pull the report for Canonicalised vs Non-Canonicalised pages. For every URL declared in an hreflang set, confirm its own canonical points to itself.

Every mismatch is a broken translated page silently missing from the index.

How to never deal with this again

The cleanest way is to have a single system generating both tags from one source of truth. When SiteDialect serves a translated version of your page, it emits a self-referential canonical and the correct hreflang cluster in one pass. The tags can't disagree because they're written by the same code, reading the same URL registry, at the same moment.

If you've been bitten by this — the "we translated the pages six months ago but they still aren't indexed" conversation — check canonical first. It's almost always canonical.

Let canonical and hreflang stop arguing

SiteDialect emits both tags from one source. Self-referential canonicals. Symmetric hreflang. No manual bookkeeping.

Get Started Free