Fix Invalid hreflang Tags for Better International SEO

Learn how to fix hreflang validation errors including unexpected language codes and relative URLs in your multi-language site.
Harlan WiltonHarlan Wilton5 min read Published

The hreflang attribute tells search engines which version of a page to show users based on their language or region. When it's misconfigured, Google might show French users your English page—or ignore your translations entirely.

What's the Problem?

Lighthouse flags two specific issues with hreflang:

1. Unexpected Language Code

Invalid or misspelled language codes mean Google can't identify the language.

<!-- Wrong: "uk" is the country code for Ukraine, not United Kingdom -->
<link rel="alternate" hreflang="uk" href="https://example.com/en-gb/" />

<!-- Wrong: invented language code -->
<link rel="alternate" hreflang="esp" href="https://example.com/es/" />

2. Relative href Value

The href must be an absolute URL. Relative URLs are rejected.

<!-- Wrong: relative URL -->
<link rel="alternate" hreflang="es" href="/es/" />

<!-- Right: absolute URL -->
<link rel="alternate" hreflang="es" href="https://example.com/es/" />

These mistakes are common because hreflang has strict syntax requirements that aren't obvious.

How to Identify This Issue

Chrome DevTools

  1. Open DevTools (F12) → Elements tab
  2. Search for hreflang in the HTML
  3. Verify each link element has:
    • A valid language code (ISO 639-1) or language-region code
    • An absolute URL starting with https:// or http://

You can also check the response headers for HTTP header-based hreflang:

  • Network tab → select the document → Headers → Look for Link: headers

Lighthouse

Run a Lighthouse SEO audit. Look for "Document doesn't have a valid hreflang" which shows each failing element with the specific error (unexpected language or relative URL).

The Fix

1. Use Valid Language Codes

Language codes must follow ISO 639-1 (two-letter codes) or BCP 47 format.

<!-- Valid language codes -->
<link rel="alternate" hreflang="en" href="https://example.com/" />
<link rel="alternate" hreflang="es" href="https://example.com/es/" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/" />
<link rel="alternate" hreflang="zh" href="https://example.com/zh/" />

<!-- Valid language-region codes -->
<link rel="alternate" hreflang="en-US" href="https://example.com/en-us/" />
<link rel="alternate" hreflang="en-GB" href="https://example.com/en-gb/" />
<link rel="alternate" hreflang="pt-BR" href="https://example.com/pt-br/" />
<link rel="alternate" hreflang="zh-TW" href="https://example.com/zh-tw/" />

Common mistakes to avoid:

  • Use en-GB not uk for UK English
  • Use zh-TW not tw for Traditional Chinese
  • Use es not esp for Spanish
  • Use pt not por for Portuguese

2. Always Use Absolute URLs

Every href must include the full URL with protocol and domain.

<!-- Before: relative URLs -->
<link rel="alternate" hreflang="en" href="/" />
<link rel="alternate" hreflang="es" href="/es/" />

<!-- After: absolute URLs -->
<link rel="alternate" hreflang="en" href="https://example.com/" />
<link rel="alternate" hreflang="es" href="https://example.com/es/" />

If you're generating hreflang tags dynamically, construct the full URL:

const baseUrl = 'https://example.com'
const hreflangs = [
  { lang: 'en', path: '/' },
  { lang: 'es', path: '/es/' },
  { lang: 'fr', path: '/fr/' },
]

hreflangs.map(h => ({
  rel: 'alternate',
  hreflang: h.lang,
  href: `${baseUrl}${h.path}`,
}))

3. Include x-default for Fallback

Add x-default to specify which page to show when no language matches.

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

Typically, x-default points to your primary language version or a language selector page.

4. Add hreflang to All Alternate Pages

Every page in your hreflang set must include links to all other versions, including itself.

<!-- On https://example.com/ (English) -->
<link rel="alternate" hreflang="en" href="https://example.com/" />
<link rel="alternate" hreflang="es" href="https://example.com/es/" />

<!-- On https://example.com/es/ (Spanish) - must have the same links -->
<link rel="alternate" hreflang="en" href="https://example.com/" />
<link rel="alternate" hreflang="es" href="https://example.com/es/" />

If the Spanish page doesn't link back to the English page, Google may ignore the hreflang entirely.

5. Choose the Right Implementation

You can implement hreflang in three places:

HTML head (most common):

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

HTTP headers (for non-HTML files like PDFs):

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

XML sitemap (for large sites):

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

Pick one method and stick with it. Lighthouse only checks HTML head and HTTP headers—not sitemaps.

Framework-Specific Solutions

Next.js — Use next-intl or similar i18n library that handles hreflang automatically. Or add links in your _document.js or via the Head component with computed absolute URLs from your domain config.
Nuxt — Use @nuxtjs/i18n module which generates correct hreflang tags automatically. Set baseUrl in your i18n config to ensure absolute URLs are generated.

Verify the Fix

  1. Run Lighthouse SEO audit again
  2. Confirm "Document has a valid hreflang" shows as passing
  3. Use Google Search Console → URL Inspection to verify Google sees your hreflang
  4. Check the International Targeting report in Search Console for hreflang errors

Common Mistakes

  • Forgetting the self-referencing link — Each page must include an hreflang link to itself. The English page needs hreflang="en" pointing to itself, not just to other languages.
  • Mixing implementation methods — Don't use HTML head on some pages and HTTP headers on others. Pick one method for consistency.
  • Not updating hreflang when URLs change — When you change a URL structure, update all hreflang links across all language versions. Broken links break the entire hreflang cluster.
  • Using hreflang in body — Hreflang links must be in <head> or HTTP headers. Links in <body> are ignored by Lighthouse and may be ignored by search engines.

Hreflang issues often appear alongside:

  • Canonical — Canonical URLs must match hreflang URLs
  • HTML Lang — The html lang attribute must match hreflang language
  • Meta Description — Each language version needs unique descriptions

Test Your Entire Site

Hreflang issues compound across a multilingual site. If your English-to-Spanish links are broken, every page in both languages is affected. Unlighthouse scans your entire site and identifies every page with hreflang errors, so you can fix the pattern once rather than hunting page by page.