You declared hreflang on every page — en points to the English version, zh points to the Chinese version, both reference each other. Then Search Console’s International Targeting report (or a third-party hreflang validator) flags missing x-default. Worse: users from India, Brazil, or Germany Google your brand and land on the Chinese page, bounce, and your bounce rate climbs.
x-default is the hreflang value that controls what users outside your declared locales see. It is not optional once your audience extends beyond the languages you’ve listed. This article walks through what it actually does, the common configurations that break, and copy-ready fixes.
What x-default actually means
From Google’s documentation: x-default declares the page to serve to users whose browser language preferences do not match any of the locales you’ve declared via hreflang. It is not “the crawler’s fallback.” It is not “the homepage.” It is the page-level default for that specific piece of content.
Example: a user in Germany browses to yourdomain.com/article with Accept-Language: de. You have en and zh declared but no de. Without x-default, Google chooses one — usually based on signals like which version has more backlinks, which usually means Google guesses wrong. With x-default: en, Google reliably serves the English page.
How to identify which case you’re in
Case 1: Missing x-default entirely
How to spot it:
curl -s https://yourdomain.com/en/article/ | grep hreflang
# Returns:
# <link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/">
# <link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/">
# (no x-default line)
Search Console International Targeting shows “missing x-default” warnings. The site still works for en and zh users, but third-locale users get arbitrary routing.
Why it happens: most CMS hreflang plugins generate one link per declared locale and never emit x-default because no one configured it. Hand-written templates often forget it.
Fix: add x-default pointing to your default version (usually English):
<link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/">
<link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/">
<link rel="alternate" hreflang="x-default" href="https://yourdomain.com/en/article/">
x-default can (and usually should) point to the same URL as one of your declared locales. The English-page URL appears twice — once as hreflang="en" and once as hreflang="x-default". That is correct.
Case 2: x-default points to the homepage instead of the equivalent page
How to spot it: on a deep article URL, view source and check the hreflang block:
<!-- on /en/article/some-post -->
<link rel="alternate" hreflang="en" href="https://yourdomain.com/en/article/some-post/">
<link rel="alternate" hreflang="zh" href="https://yourdomain.com/zh/article/some-post/">
<link rel="alternate" hreflang="x-default" href="https://yourdomain.com/"> <!-- BAD -->
Why it happens: someone misread the spec as “x-default is the site’s default page.” It is not — it is the equivalent default version of the current page.
The damage: a German user clicks a search result for “your-article topic,” lands on your homepage instead of the article they searched for, and bounces. You’re losing every long-tail visitor outside your declared locales.
Fix: x-default must point to the page-level default. On /en/article/some-post/, x-default points to /en/article/some-post/, not /.
Case 3: x-default points to a redirect
How to spot it:
curl -sI "https://yourdomain.com/article" | head -1
# HTTP/2 301
Your x-default is https://yourdomain.com/article but that URL 301s to https://yourdomain.com/en/article/. Google does follow the redirect, but the signal is weakened and some validators flag it.
Why it happens: legacy URL structure where /article used to be the English page before you introduced /en/ and /zh/ subdirectories. The redirect was set up but x-default was never updated.
Fix: point x-default to the final destination URL, not a redirect.
Case 4: Different pages in the same site declare x-default inconsistently
How to spot it: crawl your sitemap and grep all x-default declarations.
node -e "
const xml = await (await fetch('https://yourdomain.com/sitemap.xml')).text();
const urls = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map(m => m[1]);
for (const u of urls) {
const html = await (await fetch(u)).text();
const m = html.match(/hreflang=[\"']x-default[\"']\s+href=[\"']([^\"']+)[\"']/);
console.log(u, '->', m ? m[1] : '(missing)');
}
" > x-default-audit.tsv
Then sort and look for outliers. If 90% of pages have x-default pointing to /en/ equivalents but a handful point to /zh/ or to the homepage, those are mistakes.
Why it happens: a CMS plugin update changed defaults; a few pages were hand-edited.
Fix: regenerate hreflang for the whole site from one source of truth (see “Shortest fix path” below).
Case 5: x-default in sitemap conflicts with x-default in head
How to spot it: the page HTML says x-default = /en/article/, but sitemap.xml declares x-default = /zh/article/ via <xhtml:link>. Google logs a warning and picks one inconsistently.
Why it happens: two different systems emit hreflang (the CMS for the page head, a separate sitemap generator), and they disagree.
Fix: emit hreflang from one source, not two. Either head tags or sitemap entries — pick one channel.
Shortest fix path
In hit-rate order:
- Add the
x-defaultline if missing → resolves the Search Console warning instantly and routes third-locale users to your English page on next crawl. - Verify
x-defaultpoints to the page-level equivalent, not the homepage → fixes the silent UX bleed. - Regenerate all hreflang tags from a single helper → prevents drift across templates.
- Validate with the Search Console “International Targeting” report (after 1–2 weeks for re-crawl) → confirms Google sees consistent hreflang now.
A single source of truth for hreflang
Build all hreflang and x-default from one function so every page is structurally identical:
// src/lib/hreflang.js
const BASE = "https://yourdomain.com";
const DEFAULT_LANG = "en";
export function buildHreflang(currentLang, slug, availableLangs) {
const tags = availableLangs.map((lang) => ({
hreflang: lang,
href: `${BASE}/${lang}/${slug}/`,
}));
tags.push({
hreflang: "x-default",
href: `${BASE}/${DEFAULT_LANG}/${slug}/`,
});
return tags;
}
In your layout:
---
import { buildHreflang } from "../lib/hreflang.js";
const tags = buildHreflang(lang, slug, ["en", "zh"]);
---
{tags.map((t) => (
<link rel="alternate" hreflang={t.hreflang} href={t.href} />
))}
Every page renders the same shape: one entry per locale, plus x-default pointing to the English equivalent of the current page. No hand-edits.
A build-time check
Add a prebuild check that fails the build if any HTML output is missing x-default:
// scripts/check-x-default.mjs
import fg from "fast-glob";
import fs from "node:fs";
const files = fg.sync("dist/**/*.html");
const missing = [];
for (const f of files) {
const html = fs.readFileSync(f, "utf8");
const hasHreflang = /hreflang=["'](en|zh)["']/.test(html);
const hasXDefault = /hreflang=["']x-default["']/.test(html);
if (hasHreflang && !hasXDefault) missing.push(f);
}
if (missing.length) {
console.error("Missing x-default:\n" + missing.join("\n"));
process.exit(1);
}
Prevention
- Always emit hreflang from one helper — no per-template hand-writing.
x-defaultpoints to the page-level equivalent, not the site homepage.x-defaultURL must return 200 directly — never a redirect chain.- Validate with Search Console’s International Targeting report after every template change.
- If you add a third locale, update the helper, not 1000 templates.
FAQ
Q: Is x-default required?
A: Not strictly — Google will work without it. But for any site with users outside your declared locales (most public-facing sites), it is strongly recommended. Without it, Google guesses, and the guess is often wrong.
Q: Can x-default point to the same URL as an existing language?
A: Yes — that is the standard pattern. Typically x-default and hreflang="en" both point to the English page. Same URL, two <link> entries, both valid.
Q: Does x-default redirect users automatically?
A: No. x-default only tells Google which version to surface in search results for users without a locale match. It does not perform a redirect. If you want browser-level locale negotiation, implement Accept-Language-based routing separately.
Q: My hreflang is in the sitemap, not the page head. Do I still need x-default?
A: Yes. The xhtml:link tags in sitemap.xml follow the same rules — you should include an x-default entry alongside the per-locale entries.
Q: I see “hreflang tag is invalid” in Search Console. Is x-default the cause?
A: Possibly. The most common causes: (1) x-default missing, (2) x-default pointing to a 4xx URL, (3) the target URL doesn’t reciprocate (the page x-default points to must include this page in its own hreflang block). See hreflang warning in Search Console for the full diagnostic flow.
Related articles
- Hreflang warning in Search Console
- Canonical mismatch in bilingual sites
- Canonical misconfigured: 3 failure modes
- Why your new website is not showing up on Google
- Alternate page with proper canonical tag
Tags: #SEO #Troubleshooting #Debug #Structured data #hreflang #x-default #Bilingual