Skip to content
ExponaBLOG
TECHNICAL SEO FOR DEVELOPERS · DAY 055 / 100

hreflang with x-default: a practical Technical SEO guide

The fallback that catches everyone else.

Expona Admin7 min read

The hreflang problem nobody gets right on day one

Most teams deploy multilingual sites without understanding how search engines actually discover and rank alternative versions. The hreflang with x-default: a practical Technical SEO guide approach solves a genuine problem—Google needs to know which version to show to which user, and what to do when none of the specific language versions match. We've seen production sites lose 30–40% of international traffic because they misconfigured hreflang, or left it out entirely. This guide walks through the mechanics that actually work.

Every version lists every other version

The first principle feels counterintuitive: if you have English, Tamil, and Spanish versions of a page, then every single one of those pages must declare links to all the others, including itself.

This is called the "self-referential" model. On your English page at /en/, you must link to your Tamil version at /ta/, your Spanish version, and link to itself as English. This mutual declaration tells Google that these versions are equivalent. If you skip linking back to English from Tamil, Google may not treat them as alternative versions—it might crawl and index them as separate sites competing for the same content.

Here's a realistic scenario: a travel SaaS platform serves users in India (Tamil), UK (English), and Spain (Spanish). The engineering team builds three locale directories:

html
/en/destinations/london/
/ta/destinations/london/
/es/destinations/london/

Each page must include the same hreflang block, with all three versions listed:

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

This exact block goes in the <head> of the English page, the Tamil page, and the Spanish page. If your Tamil page omits English, Google starts to distrust the relationship.

Reciprocal linking is a checkpoint for Google's crawler. If the English page says "here's the Tamil version," but the Tamil page does not say "here's the English version," Google flags this as inconsistent metadata. The crawler then has to spend extra cycles figuring out whether to trust the declaration.

In teams using manual hreflang tags, reciprocal linking often breaks when:

  • Developers add a new language but forget to update the old pages' hreflang blocks.
  • One page gets migrated or renamed without updating its reciprocal partners.
  • Different teams manage different regions and don't coordinate hreflang changes.

The solution is automated generation. If you store your language configuration in a single source of truth—say, a JSON config or database record—then your build process or middleware can regenerate hreflang tags consistently across all versions.

Here's what that looks like in a real system. Suppose you have a config file:

html
// locales.config.json
{
  "locales": [
    { "code": "en", "region": "GB", "default": false },
    { "code": "ta", "region": "IN", "default": false },
    { "code": "es", "region": "ES", "default": false }
  ],
  "baseUrl": "https://example.com"
}

A build-time function or middleware reads this config and generates identical hreflang blocks for every version, guaranteeing reciprocal links. The crawler sees consistency. Ranking signals flow properly.

x-default for the language picker or default

The x-default hreflang value is a fallback mechanism. It tells Google: "If the user's language and region don't match any of my explicit hreflang entries, show them this version."

Think of it as a catch-all. If a user in Japan visits your site, and you have English, Tamil, and Spanish versions but nothing for Japanese, Google falls back to the x-default version rather than randomly choosing one or showing the English version.

Where x-default points varies by business logic:

  • Language picker first: Point x-default to a neutral landing page with a language/region selector, letting users choose. This works for travel platforms, marketplaces, or global SaaS.
  • Company default language: Point x-default to your primary language version (often English). This works if your site is primarily for English speakers with localized versions as secondary offerings.
  • Root domain fallback: Point x-default to / (the site root), which then redirects or displays a language picker based on HTTP headers or client-side detection.

For the travel SaaS example, the team decides that users outside their three target regions should land on a language picker:

html
<link rel="alternate" hreflang="en" href="https://example.com/en/destinations/london/" />
<link rel="alternate" hreflang="ta" href="https://example.com/ta/destinations/london/" />
<link rel="alternate" hreflang="es" href="https://example.com/es/destinations/london/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/language-selector/" />

The /language-selector/ page uses geolocation or manual selection to route users to the right locale. This prevents Google from arbitrarily assigning international traffic to the wrong version.

Generate from a single locale config

Manual hreflang management doesn't scale. The moment you add a fourth language or fix a URL structure, you have to edit four separate pages—and inevitably, you miss one.

Production teams use a single source of truth: a config file, database table, or even a simple TypeScript constant that defines all supported locales and their URL patterns.

Here's a concrete pattern from a NestJS backend:

html
// locales.ts
export const SUPPORTED_LOCALES = [
  { code: 'en', region: 'GB', name: 'English' },
  { code: 'ta', region: 'IN', name: 'Tamil' },
  { code: 'es', region: 'ES', name: 'Spanish' },
];

export const DEFAULT_LOCALE = 'en';
export const X_DEFAULT_URL = 'https://example.com/language-selector/';

// hreflang.service.ts
export function generateHreflangs(
  currentPath: string,
  currentLocale: string,
  baseUrl: string
): string {
  const tags = SUPPORTED_LOCALES.map(locale => {
    const url = `${baseUrl}/${locale.code}${currentPath}`;
    return `<link rel="alternate" hreflang="${locale.code}" href="${url}" />`;
  }).join('\n');

  const xDefaultTag = `<link rel="alternate" hreflang="x-default" href="${X_DEFAULT_URL}" />`;
  return tags + '\n' + xDefaultTag;
}

A middleware or template helper calls this function and injects the hreflang block into every page's <head>. When you add a new locale, you update the config once, and every page automatically includes it in the hreflang declarations. Reciprocal links are guaranteed.

For a React/Next.js frontend serving multiple locales, a similar approach uses a config and a hook:

html
// lib/locales.ts
export const locales = ['en', 'ta', 'es'] as const;
export const baseUrl = 'https://example.com';

// components/HrefLang.tsx
import Head from 'next/head';
import { locales, baseUrl } from '@/lib/locales';

export function HrefLangTags({ pathname }: { pathname: string }) {
  return (
    <Head>
      {locales.map(locale => (
        <link
          key={locale}
          rel="alternate"
          hrefLang={locale}
          href={`${baseUrl}/${locale}${pathname}`}
        />
      ))}
      <link
        rel="alternate"
        hrefLang="x-default"
        href={`${baseUrl}/language-selector/`}
      />
    </Head>
  );
}

This component renders on every page. Maintenance is centralized. The hreflang block is always consistent and complete.

Common mistakes

  • One-way hreflang links: Adding hreflang only to your English page, not to the Tamil and Spanish pages. Google sees inconsistency and deprioritizes the relationship.
  • Forgetting x-default entirely: If a user's language or region doesn't match an explicit hreflang, Google either guesses or shows a random version. Always include x-default.
  • x-default pointing to a version, not a fallback: Setting hreflang="x-default" to your English page when it's also an explicit hreflang="en" confuses the crawler. x-default should be a neutral fallback or a language picker.
  • Mismatched URL schemes: Declaring hreflang URLs with http when your site uses https, or forgetting the trailing slash. Google treats these as different URLs.
  • Locale in the domain vs. path inconsistency: Mixing example.com/en/ with en.example.com/ in the same hreflang block. Pick one structure and stick with it.
  • Outdated x-default after migrations: After rebranding or restructuring, the x-default URL points to a 404 page. Audit and update it immediately.

Key takeaways

  • Every locale version must include hreflang links to all other versions, plus x-default. Reciprocal declarations are non-negotiable for Google's crawler.
  • x-default is the fallback for users whose language or region don't match any explicit hreflang. Point it to a language picker or your primary fallback version, never to a locale-specific page.
  • Automate hreflang generation from a single locale config file or constant. Manual maintenance across multiple pages leads to inconsistency and lost traffic.
  • Use absolute URLs (with scheme and domain) in hreflang tags. Relative URLs are treated as incomplete.
  • Test hreflang with Google Search Console's URL inspection tool and validate consistency across all locale versions before shipping to production.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles