Index of every page on this site: llms.txt

Hreflang Tags in Nuxt

Configure hreflang in Nuxt with useHead or @nuxtjs/i18n to identify matching language and regional versions for Google.
Harlan Wilton12 mins read Published Updated
What you'll learn
  • Hreflang identifies equivalent language or regional versions. It helps Google choose an appropriate version; it does not guarantee which page appears.
  • Every page in a hreflang cluster needs a self-referencing tag and must link back to every alternate version it references
  • Set x-default so visitors whose language doesn't match any configured version land somewhere sensible
  • Configure locale language tags and baseUrl, then apply useLocaleHead() to generate locale SEO tags

Hreflang tags tell search engines which language version of your page to show users. They help Google identify matching regional and language versions. Fully translated main content is not duplicate content merely because it covers the same topic.

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

Google recommends hreflang when you have:

  • Same content in different languages
  • Regional variations (en-US vs en-GB)
  • Partial translations mixed with original language

Hreflang is a signal, not a directive. Search engines can ignore it if they think a different version better matches user intent.

Quick Reference

useHead
useHead({
  link: [
    { rel: 'alternate', hreflang: 'en', href: 'https://example.com/en' },
    { rel: 'alternate', hreflang: 'fr', href: 'https://example.com/fr' },
    { rel: 'alternate', hreflang: 'x-default', href: 'https://example.com/en' }
  ]
})

Hreflang Format

Hreflang values combine an ISO 639-1 language code with an optional ISO 3166-1 Alpha-2 region code:

FormatExampleUse Case
Language onlyenEnglish content for all regions
Language + regionen-USEnglish for United States
Language + regionen-GBEnglish for United Kingdom
Special fallbackx-defaultDefault for unmatched languages/regions

Use conventional casing: lowercase language and uppercase region, separated by a hyphen, such as en-US. Language tags are case-insensitive.

Common Examples

en          English (all regions)
fr          French (all regions)
es          Spanish (all regions)
en-US       English for USA
en-GB       English for UK
fr-CA       French for Canada
es-MX       Spanish for Mexico
zh-CN       Simplified Chinese (China)
zh-TW       Traditional Chinese (Taiwan)

Region Codes Are Tricky

UK isn't a valid ISO 3166-1 region code: use GB for the United Kingdom instead. For Chinese, Google's own hreflang examples use zh-TW (the script is inferred as Traditional) or the explicit script codes zh-Hant and zh-Hans, since ISO 639-1 only defines one zh language code.

Manual Implementation

The static useHead() example above is suitable when you have a known set of equivalent URLs. For dynamic pages, build alternates from your locale-aware route mapping. Do not prepend another locale to an already localized path.

Automatic Implementation with @nuxtjs/i18n

The current i18n SEO guide uses locale objects with a language field, a baseUrl, and useLocaleHead() setup. Follow that setup for your installed module version, then inspect the rendered tags.

A locale list alone is not proof that alternate links are emitted. Check self references and reciprocal links on two matching localized pages.

URL Strategies

The i18n module supports different URL patterns:

StrategyExampleNotes
prefix/en/about, /fr/aboutDefault locale also gets prefix
prefix_except_default/about, /fr/aboutDefault locale has no prefix
prefix_and_default/about, /en/about, /fr/aboutBoth work for default
no_prefix/aboutLocale in cookie/domain only
// nuxt.config.ts
export default defineNuxtConfig({
  i18n: {
    strategy: 'prefix_except_default', // /about (en), /fr/about
    defaultLocale: 'en'
  }
})

Domain-Based Locales

For sites with different domains per language, follow the i18n different-domains setup. Configure current locale language fields and apply useLocaleHead() as described in the SEO guide. Inspect matching pages on each domain; configuration alone does not establish emitted alternate tags.

X-Default Tag

The x-default annotation identifies a fallback URL for users whose language is not covered. Google recommends setting it whenever a hreflang cluster doesn't cover every possible visitor.

<!-- User with no matching language sees this -->
<link rel="alternate" hreflang="x-default" href="https://example.com/en" />

Set x-default to:

  • Your primary language version
  • A language selector page
  • The most universally understood language version
// Point x-default to language selector
useHead({
  link: [
    { rel: 'alternate', hreflang: 'en-US', href: 'https://example.com/en-us' },
    { rel: 'alternate', hreflang: 'en-GB', href: 'https://example.com/en-gb' },
    { rel: 'alternate', hreflang: 'fr-FR', href: 'https://example.com/fr-fr' },
    { rel: 'alternate', hreflang: 'x-default', href: 'https://example.com/choose-language' }
  ]
})

Hreflang Rules

Every page referenced in hreflang must link back: if page A links to page B, page B must link to page A. Missing return links is one of the mistakes Google calls out directly.

<!-- en page must reference fr page -->
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />

<!-- fr page must reference en page -->
<link rel="alternate" hreflang="en" href="https://example.com/en" />

Each page must include a hreflang tag pointing to itself:

<!-- On the English page -->
<link rel="alternate" hreflang="en" href="https://example.com/en" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />

<!-- On the French page -->
<link rel="alternate" hreflang="en" href="https://example.com/en" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />

3. Use Canonical URLs Only

All hreflang URLs must:

  • Return HTTP 200 status
  • Be indexable (no noindex)
  • Not be blocked by robots.txt
  • Point to canonical URLs (not redirects)

Make referenced alternate URLs crawlable and indexable. Missing return links can cause Google to ignore the affected annotations.

<script setup lang="ts">
// ✅ Good - points to canonical URL
useHead({
  link: [
    { rel: 'canonical', href: 'https://example.com/en/products' },
    { rel: 'alternate', hreflang: 'en', href: 'https://example.com/en/products' },
    { rel: 'alternate', hreflang: 'fr', href: 'https://example.com/fr/produits' }
  ]
})

// ❌ Bad - points to redirect
useHead({
  link: [
    { rel: 'alternate', hreflang: 'en', href: 'https://example.com/old-url' } // redirects to /en/products
  ]
})
</script>

4. Canonical vs Hreflang

Canonical tags and hreflang serve different purposes: don't use canonical to point between language versions, since that signals duplicate content rather than translations.

<!-- ✅ Correct: each language version is canonical to itself -->
<!-- English page -->
<link rel="canonical" href="https://example.com/en/about" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos" />

<!-- French page -->
<link rel="canonical" href="https://example.com/fr/a-propos" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/a-propos" />
<!-- ❌ Wrong: canonical pointing between languages -->
<!-- French page -->
<link rel="canonical" href="https://example.com/en/about" />
<!-- This tells Google the French page is duplicate content -->

Include Hreflang in the Initial Response

Prefer hreflang tags in the initial HTML response. Google can render JavaScript, but initial tags also reach clients that only read the response. Setting them from onMounted() runs after the server has already sent its response, so search engines relying on the initial HTML never see them:

<script setup lang="ts">
// ❌ Runs after SSR, search engines may miss this
onMounted(() => {
  useHead({
    link: [
      { rel: 'alternate', hreflang: 'en', href: 'https://example.com/en' }
    ]
  })
})

// ✅ Runs during SSR and client-side
useHead({
  link: [
    { rel: 'alternate', hreflang: 'en', href: 'https://example.com/en' }
  ]
})
</script>

useHead() called in a component's setup function runs during SSR by default, so this is a risk if you defer the call into a lifecycle hook or a .client.vue component.

Implementation Methods

You can implement hreflang using HTML <link> tags, HTTP headers, or XML sitemaps. Google treats these methods as equivalent. One method is easier to maintain. If you use several, keep their locale mappings consistent.

Most flexible approach. Set per-page using useHead():

<script setup lang="ts">
useHead({
  link: [
    { rel: 'alternate', hreflang: 'en-US', href: 'https://example.com/us' },
    { rel: 'alternate', hreflang: 'en-GB', href: 'https://example.com/uk' },
    { rel: 'alternate', hreflang: 'x-default', href: 'https://example.com/us' }
  ]
})
</script>

HTTP Headers

For PDFs, return a Link response header listing each matching document, including itself. The file-serving implementation belongs to your app:

Link: <https://example.com/en/document.pdf>; rel="alternate"; hreflang="en", <https://example.com/fr/document.pdf>; rel="alternate"; hreflang="fr"

XML Sitemap

Configure the Sitemap i18n integration for your module version. Check that each emitted URL lists itself and matching localized URLs. These annotations must match your locale-aware route mapping.

Common Mistakes

<!-- ❌ English page links to French, but French doesn't link back -->
<!-- en page -->
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />

<!-- fr page -->
<!-- Missing hreflang links! -->

Non-Canonical URLs

<!-- ❌ Linking to URL that redirects -->
<link rel="alternate" hreflang="en" href="https://example.com/en-us" />
<!-- But /en-us redirects to /us -->

<!-- ✅ Use final destination -->
<link rel="alternate" hreflang="en" href="https://example.com/us" />

Noindex or Blocked Pages

<!-- ❌ Hreflang on noindex page -->
<meta name="robots" content="noindex" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />

All pages in a hreflang cluster must be indexable and crawlable. Remove noindex, unblock the URL in robots.txt, or remove the page from hreflang annotations altogether.

Mixing with Canonical

<!-- ❌ Don't point canonical between languages -->
<!-- French page -->
<link rel="canonical" href="https://example.com/en" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />
<!-- Conflicting signals: canonical says "I'm a duplicate of EN"
     but hreflang says "I'm the French version" -->

Testing Hreflang

Manual Inspection

View page source and verify:

  • All alternate links are present
  • Self-referential link exists
  • x-default is set
  • All URLs return 200
  • All URLs are canonical (not redirects)

Google Search Console

The International Targeting report is deprecated. Inspect rendered alternate tags directly and verify the corresponding URLs. Google continues to support hreflang.

Third-Party Tools

Check Your Multilingual Setup

Use the version-specific i18n SEO setup for your app. Supply translation files and locale-aware routes before generating alternate metadata.

After setup, fetch the English and French versions directly. Confirm their canonical URLs and reciprocal alternate links in the initial HTML.

Checklist

Checklist
  • Every page includes a self-referencing hreflang tag plus links to all alternate versions
  • Every alternate page links back (return links) to close the cluster
  • x-default points to a sensible fallback for unmatched languages
  • Hreflang URLs return 200, are indexable, and aren't blocked by robots.txt
  • Prefer a canonical in the same language. If none exists, Google permits the closest substitute language
  • Prefer initial-response hreflang and inspect the rendered annotations on every matching localized page

Localized Sitemaps

Configure the Nuxt Sitemap module's i18n integration, then inspect the emitted alternate URLs. Sitemap annotations and head annotations need consistent locale mappings:

Sitemap 
   
Powerfully flexible XML Sitemaps that integrate seamlessly.
Was this page helpful?