Your site targets en-GB, fr-FR, de-DE. A user in Brazil visits. None of your locales match. What does Google show them? Without x-default, Google guesses — often picking the wrong page. With x-default, you tell Google exactly which page to serve as fallback. One extra annotation per page, big international UX improvement.
x-default is a special hreflang value meaning "serve this URL to any user who doesn't match my other locales". Acts as the catch-all fallback. Without it, Google's behaviour for unmatched users is inconsistent.
https://example.com/
The root URL shows a country/language picker. User explicitly chooses. Best for sites with 5+ locales, complex regional differences, e-commerce with currency/shipping variations.
<link rel="alternate" hreflang="en-GB" href="https://example.com/uk/about" /> <link rel="alternate" hreflang="en-US" href="https://example.com/us/about" /> <link rel="alternate" hreflang="fr-FR" href="https://example.com/fr/about" /> <link rel="alternate" hreflang="de-DE" href="https://example.com/de/about" /> <link rel="alternate" hreflang="x-default" href="https://example.com/" />
https://example.com/en/about
Point x-default at your English-language version without region. Best for sites where English serves international users adequately and most users would prefer "just give me English" over picking explicitly.
<link rel="alternate" hreflang="en" href="https://example.com/en/about" /> <link rel="alternate" hreflang="fr-FR" href="https://example.com/fr/about" /> <link rel="alternate" hreflang="de-DE" href="https://example.com/de/about" /> <link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />
<!-- BAD: x-default to a country-specific version --> <link rel="alternate" hreflang="x-default" href="https://example.com/us/about" /> <!-- US English shown to French Canadian users? They'd prefer fr-CA fallback or picker -->
Same x-default URL on every variant page. Treat it like another locale entry in your cluster config.
// locales-config.js
export const aboutCluster = {
'en': 'https://example.com/en/about',
'fr-FR': 'https://example.com/fr/about',
'de-DE': 'https://example.com/de/about',
'x-default': 'https://example.com/en/about' // ← fallback
};
// Generator function
function renderHreflang(cluster) {
return Object.entries(cluster).map(([lang, href]) =>
`<link rel="alternate" hreflang="${lang}" href="${href}" />`
).join('\n');
}
// Output on EVERY page in the cluster, including the x-default target itself
Most WordPress translation plugins generate x-default automatically. If missing, check plugin settings:
WPML → Languages → SEO options - "Add alternate language links to the head" → enabled - "Send default language as x-default" → enabled Polylang → Languages → Settings - "Generate hreflang" → enabled - "Default language is x-default" → enabled
// app/[locale]/about/page.tsx
export async function generateMetadata({ params }) {
return {
alternates: {
canonical: `https://example.com/${params.locale}/about`,
languages: {
'en': 'https://example.com/en/about',
'fr-FR': 'https://example.com/fr/about',
'de-DE': 'https://example.com/de/about',
'x-default': 'https://example.com/en/about'
}
}
};
}
{{ range .Translations }}
<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}" />
{{ end }}
<link rel="alternate" hreflang="{{ .Lang }}" href="{{ .Permalink }}" />
<link rel="alternate" hreflang="x-default" href="{{ .Site.BaseURL }}{{ .RelPermalink | replaceRE "^/[a-z]{2}/" "/en/" }}" />
---
const locales = {
'en': '/en/about',
'fr-FR': '/fr/about',
'de-DE': '/de/about'
};
---
{Object.entries(locales).map(([lang, path]) => (
<link rel="alternate" hreflang={lang} href={`https://example.com${path}`} />
))}
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about" />
curl -s https://example.com/en/about | grep hreflang curl -s https://example.com/fr/about | grep hreflang # Both should include the same hreflang="x-default" line