Kurzfassung: hreflang-Tags teilen Suchmaschinen mit, welche Sprachversion einer Seite in den Suchergebnissen angezeigt werden soll. Für Single-Page-Anwendungen (SPAs) erfordert eine korrekte hreflang-Implementierung eine dynamische Tag-Injection über den
<head>, konsistente kanonische URL-Muster und sprachspezifische Sitemaps. Dieser Leitfaden deckt alles ab – von der grundlegenden Tag-Syntax bis hin zu fortgeschrittenen Mustern für SPAs, die mit TanStack Router oder React Router erstellt wurden, einschließlich automatischer Spracherkennung, Fallback-Hierarchien und Google Search Console-Verifizierung.
Was sind hreflang-Tags und warum sind sie wichtig?
hreflang ist ein HTML-Attribut, das Suchmaschinen die Beziehung zwischen Seiten in verschiedenen Sprachen mitteilt. Wenn ein Nutzer auf Deutsch nach „SaaS-Preise" sucht, verwendet Google hreflang, um die deutsche Version Ihrer Seite anzuzeigen – selbst wenn die englische Version über eine höhere Domain-Autorität verfügt.
Die korrekte Implementierung von hreflang-Tags ist ein entscheidender Bestandteil jeder technischen Globalisierungsstrategie. Ohne sie kann mehrsprachiger Content unter Duplicate-Content-Strafen, falschen Sprach-Rankings und verschwendetem Crawl-Budget leiden – all das schwächt Ihre internationalen SEO-Bemühungen.
Ohne hreflang treten bei mehrsprachigen Websites drei Probleme auf:
| Problem | SEO-Auswirkung | Lösung |
|---|---|---|
| Doppelter Content | Google sieht /en/pricing und /de/preise als dieselbe Seite – Ranking-Signale werden verwässert | hreflang teilt Google mit, dass es sich um Übersetzungen handelt, nicht um Duplikate |
| Falsches Sprach-Ranking | Englische Seite rankt für deutsches Keyword, was zu einer hohen Absprungrate führt | hreflang leitet Nutzer an ihre Sprache |
| Verschwendung des Crawl-Budgets | Googlebot crawlte jede Sprachvariante jeder Seite | Korrekte Annotationen konsolidieren Crawl-Signale |
Eine Studie von Aleyda Solis aus dem Jahr 2025 ergab, dass 63 % der mehrsprachigen Websites noch immer eine fehlerhafte oder fehlende hreflang-Implementierung aufweisen – was dies zu einem schnellen SEO-Gewinn für Websites macht, die es richtig umsetzen.
hreflang-Tag-Syntax und -Muster
Grundlegende Syntax
<link rel="alternate" hreflang="en" href="https://tanstackship.com/en/page" />
<link rel="alternate" hreflang="de" href="https://tanstackship.com/de/page" />
<link rel="alternate" hreflang="zh" href="https://tanstackship.com/zh/page" />
<link rel="alternate" hreflang="x-default" href="https://tanstackship.com/en/page" />
Zwei Implementierungsmethoden
| Methode | Wo | Vorteile | Nachteile |
|---|---|---|---|
HTML-<link>-Tags | Im <head> jeder Seite | Einfach, funktioniert überall | Erfordert dynamische Injection pro Seite |
| XML-Sitemaps | In sitemap.xml mit xhtml:link | Zentrale Verwaltung, keine HTML-Änderungen | Google kann länger brauchen, um Seiten zu entdecken |
Best Practice: Beides verwenden. HTML-Tags für schnelle Entdeckung, Sitemaps für Vollständigkeit.
hreflang in SPAs implementieren: Die Herausforderung
SPAs rendern JavaScript clientseitig, was bedeutet, dass statische HTML-Dateien keine dynamischen hreflang-Tags enthalten können. Es gibt drei Ansätze:
Ansatz 1: Serverseitige Injection (Empfohlen für TanStack Start)
Mit dem SSR von TanStack Start können hreflang-Tags serverseitig injiziert werden, bevor das HTML den Client erreicht. Dieser Ansatz stellt sicher, dass Suchmaschinen-Crawler vollständige hreflang-Markups in der ersten Antwort erhalten, wodurch die mit clientseitiger Hydrierung verbundenen SEO-Risiken vermieden werden. Eine umfassende Anleitung zum Erstellen einer Full-Stack-Anwendung mit SSR finden Sie im TanStack Start-Tutorial.
Der folgende Code zeigt das hreflang-Generierungsutility und seine Integration in die Root-Route:
// src/lib/hreflang.ts
interface HreflangConfig {
baseUrl: string
currentPath: string
locales: string[]
defaultLocale: string
}
export function generateHreflangLinks(config: HreflangConfig): string[] {
const { baseUrl, currentPath, locales, defaultLocale } = config
// Normalize path: strip existing locale prefix if present
const pathWithoutLocale = currentPath.replace(/^\/(en|de|zh)/, "")
return [
// One link per locale
...locales.map(
(locale) =>
`<link rel="alternate" hreflang="${locale}" href="${baseUrl}/${locale}${pathWithoutLocale}" />`
),
// x-default fallback
`<link rel="alternate" hreflang="x-default" href="${baseUrl}/${defaultLocale}${pathWithoutLocale}" />`,
]
}
// src/routes/__root.tsx — inject hreflang via SSR
import { Head } from "@tanstack/react-start"
import { generateHreflangLinks } from "../lib/hreflang"
export const Route = createRootRoute({
component: () => {
const { locale } = Route.useRouteContext()
// Strip leading slash for sitemap path
const path = location.pathname
const hreflangLinks = generateHreflangLinks({
baseUrl: "https://tanstackship.com",
currentPath: path,
locales: ["en", "de", "zh"],
defaultLocale: "en",
})
return (
<html lang={locale}>
<head>
<Head />
<meta property="og:locale" content={locale === "zh" ? "zh_CN" : locale === "de" ? "de_DE" : "en_US"} />
{hreflangLinks.map((link) => (
<link
key={link.match(/hreflang="([^"]+)"/)?.[1] || Math.random()}
rel="alternate"
hrefLang={link.match(/hreflang="([^"]+)"/)?.[1]}
href={link.match(/href="([^"]+)"/)?.[1]}
/>
))}
</head>
<body>
<Outlet />
</body>
</html>
)
},
})
Die Funktion generateHreflangLinks normalisiert den aktuellen Pfad, indem sie ein vorhandenes Locale-Präfix entfernt, und generiert dann ein <link>-Tag pro unterstützter Locale plus ein x-default-Fallback. Die Root-Route-Komponente rendert diese Tags als JSX im <head> und gewährleistet so, dass jede servergelieferte Seite vollständige hreflang-Annotationen enthält.
Ansatz 2: Statische Site-Generierung (Vorgängenerstellt pro Locale)
Für statisch generierte SPAs: Generieren Sie eine HTML-Datei pro Locale pro Seite:
// scripts/generate-hreflang.ts
import fs from "fs/promises"
import path from "path"
interface PageConfig {
path: string
locales: string[]
}
async function injectHreflang(page: PageConfig) {
const { path: pagePath, locales } = page
const baseUrl = "https://tanstackship.com"
const hreflangTags = locales
.map(
(locale) =>
`<link rel="alternate" hreflang="${locale}" href="${baseUrl}/${locale}${pagePath}" />`
)
.join("\n")
// Inject into each locale's HTML file
for (const locale of locales) {
const filePath = path.join("dist", locale, pagePath, "index.html")
const html = await fs.readFile(filePath, "utf-8")
const updatedHtml = html.replace(
"</head>",
`${hreflangTags}\n<link rel="alternate" hreflang="x-default" href="${baseUrl}/${locale}${pagePath}" />\n</head>`
)
await fs.writeFile(filePath, updatedHtml)
}
}
Ansatz 3: Clientseitige dynamische Injection (Am wenigsten empfohlen)
Als letztes Mittel für rein clientseitige SPAs: Injizieren Sie Tags nach dem ersten Rendern:
// src/hooks/useHreflang.ts
import { useEffect } from "react"
import { useLocation } from "@tanstack/react-router"
export function useHreflang(locales: string[]) {
const location = useLocation()
const baseUrl = "https://tanstackship.com"
useEffect(() => {
const path = location.pathname.replace(/^\/(en|de|zh)/, "")
// Remove existing hreflang tags
document
.querySelectorAll('link[rel="alternate"][hreflang]')
.forEach((el) => el.remove())
// Inject new ones
locales.forEach((locale) => {
const link = document.createElement("link")
link.rel = "alternate"
link.setAttribute("hreflang", locale)
link.href = `${baseUrl}/${locale}${path}`
document.head.appendChild(link)
})
// x-default
const defaultLink = document.createElement("link")
defaultLink.rel = "alternate"
defaultLink.setAttribute("hreflang", "x-default")
defaultLink.href = `${baseUrl}/${locales[0]}${path}`
document.head.appendChild(defaultLink)
}, [location.pathname, locales])
}
Warnung: Die clientseitige Injection ist für SEO problematisch – Googlebot wartet möglicherweise nicht auf die JavaScript-Ausführung, bevor es den
<head>liest. Verwenden Sie nach Möglichkeit die serverseitige Injection.
Sitemap-basiertes hreflang (Der vollständige Weg)
XML-Sitemaps mit xhtml:link-Annotationen bieten ein Fallback für Seiten, die Googlebot möglicherweise übersieht:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://tanstackship.com/en/pricing</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://tanstackship.com/en/pricing"/>
<xhtml:link rel="alternate" hreflang="de" href="https://tanstackship.com/de/preise"/>
<xhtml:link rel="alternate" hreflang="zh" href="https://tanstackship.com/zh/pricing"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://tanstackship.com/en/pricing"/>
<lastmod>2026-06-09</lastmod>
</url>
<url>
<loc>https://tanstackship.com/de/preise</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://tanstackship.com/en/pricing"/>
<xhtml:link rel="alternate" hreflang="de" href="https://tanstackship.com/de/preise"/>
<xhtml:link rel="alternate" hreflang="zh" href="https://tanstackship.com/zh/pricing"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://tanstackship.com/en/pricing"/>
<lastmod>2026-06-09</lastmod>
</url>
</urlset>
Mehrsprachige Sitemaps mit TanStack Start generieren
// src/server/sitemap.ts
import { createServerFn } from "@tanstack/react-start"
interface SitemapUrl {
path: string
updatedAt: string
locale: string
}
export const generateSitemap = createServerFn({ method: "GET" }).handler(
async () => {
const baseUrl = "https://tanstackship.com"
const locales = ["en", "de", "zh"]
// Get all published blog posts with their translations
const posts = await env.DB.prepare(
`SELECT b.slug, b.updated_at, b.locale
FROM blog_posts b
WHERE b.published = 1`
).all()
// Build URL groups (same content, different languages)
const urlGroups = buildUrlGroups(posts.results as SitemapUrl[], baseUrl)
return generateSitemapXml(urlGroups, locales, baseUrl)
}
)
Häufige hreflang-Fehler (und wie Sie diese beheben)
Fehler 1: Fehlende Return-Tags
Wenn Seite A über hreflang auf Seite B verlinkt, muss Seite B zurück auf Seite A verlinken. Google nennt dies „bidirektionale Annotation".
<!-- ❌ FALSCH: Seite A verlinkt auf B, aber B verlinkt nicht zurück -->
<!-- Seite A: -->
<link rel="alternate" hreflang="de" href="/de/page" />
<!-- Seite B: -->
<!-- Keinerlei hreflang-Tags! -->
<!-- ✅ RICHTIG: Bidirektionale Verlinkung -->
<!-- Seite A: -->
<link rel="alternate" hreflang="en" href="/en/page" />
<link rel="alternate" hreflang="de" href="/de/page" />
<!-- Seite B: -->
<link rel="alternate" hreflang="en" href="/en/page" />
<link rel="alternate" hreflang="de" href="/de/page" />
Fehler 2: Inkonsistente Sprachcodes
<!-- ❌ FALSCH: Gemischte ISO-Formate -->
<link rel="alternate" hreflang="english" href="/en/page" />
<link rel="alternate" hreflang="de-de" href="/de/page" />
<!-- ✅ RICHTIG: ISO 639-1-Sprachcodes mit optionaler ISO 3166-1-Region -->
<link rel="alternate" hreflang="en" href="/en/page" />
<link rel="alternate" hreflang="en-US" href="/en/page" />
<link rel="alternate" hreflang="de" href="/de/page" />
<link rel="alternate" hreflang="de-DE" href="/de/page" />
<!-- De-duplizieren: Wenn zwei hreflang-Werte auf dieselbe URL verweisen, verwenden Sie die spezifischere -->
Fehler 3: Kanonische URLs im Konflikt
<!-- ❌ FALSCH: Kanonisch verweist auf eine andere Locale als hreflang -->
<link rel="canonical" href="/en/page" />
<link rel="alternate" hreflang="de" href="/de/page" />
<!-- ✅ RICHTIG: Selbstreferenzierendes Kanonisch -->
<link rel="canonical" href="/de/page" />
<link rel="alternate" hreflang="en" href="/en/page" />
<link rel="alternate" hreflang="de" href="/de/page" />
Fehler 4: hreflang mit Fragment-Identifiers verwenden
<!-- ❌ FALSCH: Google ignoriert Fragmente -->
<link rel="alternate" hreflang="de" href="/page#section2" />
<!-- ✅ RICHTIG: Vollständige URL ohne Fragment verwenden -->
<link rel="alternate" hreflang="de" href="/de/page" />
Das x-default hreflang: Wann es verwendet werden sollte
x-default teilt Google mit, welche Seite angezeigt werden soll, wenn keine Sprache den Nutzerpräferenzen entspricht. Dies ist für SPAs entscheidend, da die Browserspracherkennung möglicherweise nicht mit Ihren unterstützten Locales übereinstimmt.
<!-- x-default sollte auf Ihren häufigsten Einstiegspunkt verweisen -->
<link rel="alternate" hreflang="x-default" href="https://tanstackship.com/en" />
<!-- Für ein globales SaaS-Produkt mit Englisch als Standard -->
<link rel="alternate" hreflang="x-default" href="https://tanstackship.com/en" />
<!-- Für ein regionales Produkt: Verweis auf den wahrscheinlichsten Markt -->
<link rel="alternate" hreflang="x-default" href="https://tanstackship.com/de" />
Verwenden Sie x-default, wenn:
- Die Browsersprache des Nutzers keiner Ihrer unterstützten Sprachen entspricht
- Der Nutzer ein Suchmaschinen-Crawler ist (Googlebot setzt typischerweise
enoderen-us) - Sie eine Sprachauswahl-Landingpage wünschen (2026 weniger üblich – Nutzer bevorzugen den direkten Einstieg)
Testen und Validieren
Google Search Console
- Navigieren Sie in der Search Console zu Internationales Targeting > Sprache
- Prüfen Sie den Bericht auf Fehler wie „keine Return-Tags" und „nicht eindeutige hreflang"
- Verwenden Sie das URL-Prüftool, um hreflang auf bestimmten Seiten zu verifizieren
Automatisiertes Testskript
#!/bin/bash
# hreflang-test.sh — Validate hreflang tags on a set of URLs
BASE_URL="https://tanstackship.com"
LOCALES=("en" "de" "zh")
PAGES=("/pricing" "/features" "/blog/react-internationalization-guide-2026")
for page in "${PAGES[@]}"; do
for locale in "${LOCALES[@]}"; do
url="${BASE_URL}/${locale}${page}"
echo "Testing: $url"
# Fetch and extract hreflang tags
tags=$(curl -s "$url" | grep -o 'rel="alternate"[^>]*hreflang="[^"]*"[^>]*' | wc -l)
echo " Found $tags hreflang tags"
# Check for return links
has_return=$(curl -s "$url" | grep -c "hreflang=\"${locale}\"")
if [ "$has_return" -eq 0 ]; then
echo " ⚠️ Missing self-referencing hreflang for ${locale}"
fi
done
done
Online-Validatoren
- Aleyda Solis' hreflang Tags Generator Tool
- Merkle's hreflang Testing Tool
- DeepCrawl's International SEO module
hreflang für dynamische Inhalte (Blogbeiträge, CMS-Seiten)
Für Content-getriebene SPAs mit Blogbeiträgen in mehreren Sprachen müssen hreflang-Tags dynamisch auf Basis von Datenbankbeziehungen zwischen übersetzten Inhalten generiert werden. Wenn Sie ein mehrsprachiges Content-System implementieren, deckt unser React-Internationalisierungsleitfaden die breitere i18n-Architektur für SPAs ab, die alongside diesen hreflang-Mustern funktioniert.
Das folgende Utility ruft alle Übersetzungen für einen bestimmten Blogbeitrag ab und generiert die entsprechenden hreflang-Link-Tags:
// src/lib/blog-hreflang.ts
interface BlogPostTranslation {
locale: string
slug: string
}
export async function getBlogPostHreflang(
postId: string,
currentLocale: string
): Promise<string[]> {
const translations = await env.DB.prepare(
`SELECT locale, slug FROM blog_posts
WHERE translation_group_id = (
SELECT translation_group_id FROM blog_posts WHERE id = ?
) AND published = 1`
)
.bind(postId)
.all()
return (translations.results as BlogPostTranslation[]).map(
(t) =>
`<link rel="alternate" hreflang="${t.locale}" href="https://tanstackship.com/${t.locale}/blog/${t.slug}" />`
)
}
-- Database schema for translated blog posts
CREATE TABLE blog_posts (
id TEXT PRIMARY KEY,
translation_group_id TEXT NOT NULL,
locale TEXT NOT NULL,
slug TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
published INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
UNIQUE(locale, slug)
);
CREATE INDEX idx_blog_posts_translation_group
ON blog_posts(translation_group_id);
Das Datenbankschema speichert jeden Blogbeitrag mit einer translation_group_id, die alle Sprachvarianten desselben Contents gruppiert. Die Funktion getBlogPostHreflang fragt diese Gruppe ab, um vollständige hreflang-Annotationen zu generieren, und stellt sicher, dass jeder Blogbeitrag unabhängig davon, in welcher Sprache der Nutzer ihn betrachtet, korrekt auf seine übersetzten Gegenstücke verweist.
Leistungsauswirkung: Beeinflusst hreflang die Seitengeschwindigkeit?
hreflang-Tags sind <link>-Elemente im <head> – sie blockieren weder das Rendern noch fügen sie messbares Gewicht hinzu. Für eine typische SaaS-Site mit 3–5 Locales beträgt das gesamte hreflang-Markup weniger als 1 KB. Die eigentliche Leistungsbetrachtung betrifft die serverseitige Injection: Das Generieren von hreflang-Tags zur Anfragezeit fügt einen vernachlässigbaren Overhead hinzu (< 1 ms) im Vergleich zu den Vorteilen korrekter internationaler SEO.
Checkliste: hreflang für die Produktionsreife von SPAs
- Jede Seite hat selbstreferenzierendes hreflang (verlinkt auf ihre eigene Locale)
- Jede Seite hat kreuzreferenzierendes hreflang (verlinkt auf alle anderen Locales)
- Return-Tags sind bidirektional verifiziert (Seite A ↔ Seite B für alle Paare)
- x-default hreflang ist auf allen Seiten gesetzt
- Kanonische URLs sind pro Locale selbstreferenzierend
- Sprachcodes verwenden das gültige ISO 639-1-Format
- Sitemaps enthalten
xhtml:link-Annotationen für alle Locale-Varianten - Google Search Console zeigt keine hreflang-Fehler
- Dynamische Seiten (Blogbeiträge) generieren hreflang aus Datenbank-Übersetzungsgruppen
- Sprachwechsler aktualisiert hreflang-Tags ohne vollständigen Seitenreload
- Keine widersprüchlichen hreflang-Deklarationen in robots.txt oder HTTP-Headern
- Mit einem echten mehrsprachigen Nutzerflow in allen unterstützten Sprachen getestet
Fazit
Die hreflang-Implementierung für SPAs erfordert mehr Aufmerksamkeit als für traditionelle Multi-Page-Sites, da dynamisches clientseitiges Rendern die Tag-Entdeckung verzögern oder verhindern kann. Die wichtigsten Prinzipien sind:
- hreflang serverseitig injizieren, wann immer möglich – das SSR von TanStack Start macht dies unkompliziert
- Immer Rückverweise einschließen – bidirektionale Annotationen sind eine zwingende Anforderung, keine Empfehlung
- Sowohl HTML-Tags als auch Sitemaps verwenden – Sitemaps dienen als Fallback für Seiten, die Googlebot nicht vollständig rendert
- Kanonische URLs pro Locale selbstreferenzierend halten, um widersprüchliche Signale zu vermeiden
- Tests automatisieren – manuelle hreflang-Verifizierung skaliert nicht über eine Handvoll Seiten hinaus
Mehrsprachige SEO ist eine langfristige Investition. Eine korrekte hreflang-Implementierung stellt sicher, dass Ihr Content in jedem Sprachmarkt, den Sie anvisieren, konkurriert – ohne die Belastung durch Duplicate-Content-Strafen oder falsche Sprach-Rankings.