SPA-SEO

So machen Sie Single-Page-Anwendungen (React Router, Vue Router, Angular Router) crawlbar und indexierbar – die Probleme mit App-Shell und Soft-404, History API vs. Hash-Routing, pro Route HTML per SSR/Prerendering, pro Route Canonicals und Titel sowie Sitemap-Generierung für clientseitige Routen.

Erstveröffentlicht: 3. Juli 2026 · Zuletzt aktualisiert: 21. Aug. 2026 · Fortgeschritten
Sprachen

Eine Single-Page-Anwendung lädt ein Dokument und tauscht Ansichten mit JavaScript aus, anstatt eine neue Seite vom Server anzufordern. Viele SPAs implementieren das mit einer 'App-Shell' – der Server liefert echtes HTML für eine URL, eine fast leere Hülle, bis JS jede Route rendert – aber das ist eine häufige Implementierungsentscheidung, keine Regel, der jede SPA folgt; prüfen Sie, was eine direkte Anfrage an jede Route tatsächlich zurückgibt, bevor Sie das annehmen. Die Lösung, wo eine bloße App-Shell-Einrichtung das Problem ist, hat zwei unabhängige Hälften: Adressierbarkeit (History API, nicht Hash/#!-Fragmente, damit jede Ansicht eine echte URL hat – eine Browser-Soft-Navigation über die History API ändert URL und UI, erzeugt aber selbst keine neue Serverantwort) und Inhaltsverfügbarkeit (SSR, Prerendering oder ein Meta-Framework, damit jede dieser URLs bei Anfrage eindeutiges HTML zurückgeben kann). Darüber hinaus überprüfen Sie Titel, Canonical und Robots-Status jeder Route sowohl beim direkten Einstieg als auch nach dem Rendern, behandeln Sie Soft-404s (Router tendieren dazu, einen 200-Status für 'Nicht gefunden'-Ansichten beizubehalten – leiten Sie auf eine URL um, die selbst einen echten Fehlerstatus liefert, oder fügen Sie ein gerendertes noindex hinzu, obwohl ein anfängliches noindex dazu führen kann, dass das Rendern übersprungen wird), und stellen Sie sicher, dass jede Route in Ihrer Sitemap tatsächlich zu echtem, indexierbarem HTML auflöst – es gibt kein spezielles SPA-Sitemap-Format.

TL;DR — Das zentrale SEO-Risiko einer SPA ist nicht “JavaScript” im Allgemeinen — es ist, dass clientseitiges Routing ändert, was der Benutzer sieht, ohne zu ändern, was der Server zurückgeben würde. Die Lösung hat zwei unabhängige Hälften, die ständig verwechselt werden: Adressierbarkeit (History-API, eindeutige URLs pro Route, keine #!-Fragmente) und Inhalts- verfügbarkeit (SSR, Prerendering/SSG oder ein Meta-Framework). Nur Ersteres zu tun, ergibt eine ordentliche Sitemap von URLs, die alle dieselbe Hülle rendern. Zusätzlich zu beidem benötigt jede Route ihr eigenes Kanonisch/Titel/Beschreibung im gerenderten DOM, Sie müssen Soft-404s behandeln, die ein 200 behalten, und jede Route in der Sitemap muss unabhängig zu echtem HTML auflösen.

Was SPA-SEO tatsächlich ist

SPA ist eine Anwendungsarchitektur, kein garantierter SEO-Fehlzustand. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: MDN: Single-page application Server-Rendering, Prerendering oder sorgfältig implementiertes Client-Rendering können Inhalte zugänglich machen, aber keines garantiert Indexierung. Evidence for this claim Primary standard or official documentation supporting the adjacent article claim. Scope: Protocol semantics and Search behavior are kept separate; no indexing, ranking, or migration-timing guarantee is inferred. Confidence: high · Verified: Google: JavaScript SEO basics

Dies ist der Deep Dive speziell zu Single-Page-Anwendungen – Client-seitiges Routing mit React Router, Vue Router oder Angular Router, bei dem der Server nach dem ersten Laden nie wieder eine vollständige Seite sendet. Er steht neben dem breiteren JavaScript-SEO-Leitfaden (der Parität, Lazy-Loading, Infinite Scroll und JS-Rendering allgemein abdeckt) und den framework-spezifischen Artikeln für React, Next.js, Nuxt, Angular, Vue, Svelte und Astro. Hier interessiert mich nur die Routing-Ebene und was sie für Crawling und Indexierung bedeutet.

Warum nackte, client-seitig geroutete SPAs bei SEO scheitern

Eine URL, eine HTML-Antwort – das App-Shell-Problem

Google beschreibt den Fehlermodus präzise: “Some JavaScript sites may use the app shell model where the initial HTML does not contain the actual content and Google needs to execute JavaScript before being able to see the actual page content.” (Übersetzung) „Einige JavaScript-Websites verwenden möglicherweise das App-Shell-Modell, bei dem das anfängliche HTML nicht den eigentlichen Inhalt enthält und Google JavaScript ausführen muss, bevor es den tatsächlichen Seiteninhalt sehen kann.“ In einer nackten SPA ist die “App-Shell” alles, was der Server jemals zurückgibt. Rufen Sie /products frisch auf und Sie erhalten dasselbe fast leere Dokument, das Sie für /about erhalten würden. Der Inhalt weicht erst ab, nachdem der Browser Ihr JavaScript ausführt und Ihr Router entscheidet, was angezeigt wird.

Das ist das ganze Problem in einem Satz: Client-seitiges Routing ändert, was der Benutzer sieht, ohne zu ändern, was der Server zurückgeben würde. Alles Weitere – Soft-404s, doppelter Inhalt, fehlende Titel – ist ein Symptom davon.

Der Server sieht nie, welche “Seite” angefordert wurde (warum Hash-Routing scheitert)

Ältere SPAs laden Ansichten aus URL-Fragmenten – example.com/#/products. Googles genauer Wortlaut: “A SPA may use URL fragments (for example https://example.com/#/products) for loading different views.” (Übersetzung) „Eine SPA kann URL-Fragmente (zum Beispiel die zuvor genannte Produkt-URL) verwenden, um verschiedene Ansichten zu laden.“ Dies scheitert aus einem spezifischen, mechanischen Grund: Browser senden das Fragment (alles nach #) nie im HTTP-Request an den Server. Der Server kann buchstäblich nicht wissen, welche “Seite” angefordert wurde, also kann er keinen unterschiedlichen Inhalt oder einen unterschiedlichen Statuscode dafür zurückgeben. Ein Crawler, der die URL einmal abruft, erhält identisches HTML, unabhängig vom Fragment.

Das ist auch der Grund, warum Google sein AJAX-Crawling-Schema von 2009 im Oktober 2015 offiziell eingestellt hat: “In short: We are no longer recommending the AJAX crawling proposal we made back in 2009.” (Übersetzung) „Kurz gesagt: Wir empfehlen den AJAX-Crawling- Vorschlag, den wir 2009 gemacht haben, nicht mehr.“ Der alte _escaped_fragment_-Workaround erlaubte es Servern, Fragment-Routen auf Anfrage vorzurendern – aber er umging das Routing-Problem, anstatt es zu beheben. Googles eigene Empfehlung, die es ersetzt, ist die History API, die unten behandelt wird.

Soft-404s – Client-seitige Router behalten für alles ein 200

Dies ist in einer nackten SPA nahezu unvermeidlich. Client-seitige Router behalten per Design den 200-Status der ursprünglichen Seite für jede virtuelle Navigation – einschließlich “Nicht gefunden”-Zuständen. Google weist explizit darauf hin: “In a single-page application (SPA), this can be especially difficult. To prevent error pages from being indexed, you can use one or both of the following strategies.” (Übersetzung) „In einer Single-Page-Anwendung (SPA) kann dies besonders schwierig sein. Um zu verhindern, dass Fehlerseiten indexiert werden, können Sie eine oder beide der folgenden Strategien anwenden.“ Und das Warum: “When a SPA is using client-side JavaScript to handle errors they often report a 200 HTTP status code instead of the appropriate status code.” (Übersetzung) „Wenn eine SPA clientseitiges JavaScript zur Fehlerbehandlung verwendet, meldet sie häufig einen HTTP-Statuscode 200 anstelle des passenden Statuscodes.“

Das Ergebnis ist, dass leere oder Fehleransichten als dünne 200-Seiten indexiert werden. Google dokumentiert hier genau zwei Strategien, präzise: Leiten Sie auf eine URL um (oder machen Sie eine vollständige Anfrage), deren Server einen echten 404/Fehlerstatus zurückgibt, oder fügen Sie dem Fehleransicht mit JavaScript ein noindex-Tag hinzu. Achten Sie auf die zweite: Wenn dieses noindex vom allerersten Paint an vorhanden ist, anstatt erst hinzugefügt zu werden, nachdem die App entscheidet, dass die Route ungültig ist, kann dies dazu führen, dass Google das Rendering der Seite komplett überspringt – überprüfen Sie also, was eine direkte Anfrage bevor irgendein JS läuft zurückgibt, nicht nur, was danach im gerenderten DOM erscheint.

Evidence for this claim For a client-rendered not-found view, Google documents two approaches: redirect to a URL whose server returns a 404 response, or add noindex with JavaScript; an initial noindex can cause rendering to be skipped, so raw and rendered directives require separate testing. Scope: SPAs Confidence: high · Verified: Fix Search-related JavaScript problems

Duplikate durch gemeinsame Shell – eine mögliche Ursache, keine automatische Diagnose

Es gibt eine zweite, heimtückischere Fehlerart: Verschiedene Routen werden als Duplikate voneinander indexiert, weil sie auf dasselbe gemeinsame Header-/Navigations-/Footer-Grundgerüst reduziert werden. Gary Illyes beschrieb einen Weg, wie das passiert: “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out (my most likely explanation) and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (Übersetzung) „Ich habe eine Reihe von E-Mails in meinem Posteingang, bei denen das Problem ist, dass das Herzstück ewig zum Laden brauchte, sodass das Rendering eine Zeitüberschreitung hatte (meine wahrscheinlichste Erklärung) und wir eine Reihe von Seiten hatten, die nur das Grundgerüst enthielten. Mit nur dem Grundgerüst sind diese Seiten Duplikate.“ Sein Fix: “Try to restructure the js calls such that the content (including marginal boilerplate) loads first.” (Übersetzung) „Versuchen Sie, die JS-Aufrufe so umzustrukturieren, dass der Inhalt (einschließlich des marginalen Grundgerüsts) zuerst lädt.“ (Über einen LinkedIn-Beitrag weitergegeben, der einer automatisierten Verifizierung widersteht; als sekundäre Quelle mit hoher Vertrauenswürdigkeit behandeln.)

Beachten Sie, dass Illyes es als „meine wahrscheinlichste Erklärung“ formuliert, nicht als bestätigte Diagnose – das sollte man wörtlich nehmen. „Render-Timeout“ ist nichts, was man allein anhand der Symptome diagnostizieren kann; Sie benötigen Nachweise auf Anfrageebene (was ein direkter Abruf tatsächlich zurückgibt), Nachweise für die gerenderte Ausgabe (ob der zentrale Inhalt nach dem Rendering vorhanden ist) und Search-Console-Nachweise (Duplikat-/Indexierungssignale), bevor Sie das Duplikat-Inhaltsproblem einer Route speziell auf ein Timeout zurückführen, statt auf einen Fehler, eine blockierte Ressource oder eine wirklich identische Shell. Es gibt auch kein festes, veröffentlichtes Timeout, um das herum man planen könnte – Googles eigenes Grundlagendokument sagt, dass eine Seite “may stay on this queue for a few seconds, but it can take longer than that,” (Übersetzung) „möglicherweise einige Sekunden in dieser Warteschlange bleibt, aber es kann länger dauern“, ohne sich auf eine Zahl festzulegen. Planen Sie nicht um eine angenommene Render-Warteschlangendauer herum; stellen Sie stattdessen sicher, dass der primäre Inhalt so früh wie möglich lädt, unabhängig davon, wie lange das Rendering letztendlich dauert.

Der Fix: Echtes HTML pro Route

Der vollständige Fix hat zwei unabhängige Teile, die ständig verwechselt werden:

  1. URL-Adressierbarkeit – History API, eindeutige URLs pro Route, keine Fragmente.
  2. Inhaltsverfügbarkeit – SSR, statisches Vorrendern/SSG oder ein Meta-Framework.

Wenn Sie nur #1 tun, erhalten Sie saubere, teilbare URLs, die alle weiterhin dieselbe leere Shell zurückgeben. Für eine Route, die Sie tatsächlich indexiert haben möchten, brauchen Sie beides.

Das ist jedoch kein universelles Gebot, überall SSR einzuführen. SSR und Vorrendern reduzieren, wie stark Sie davon abhängen, dass ein Crawler Ihr JavaScript erfolgreich ausführt – sie werden nicht automatisch Pflicht, sobald eine Website technisch gesehen eine SPA ist. Bevor Sie eine Architektur für eine bestimmte Route wählen, prüfen Sie drei Dinge: ob diese Route überhaupt ranken muss (ein internes Admin-Panel tut das nicht), was eine direkte, JS-freie Anfrage an sie bereits zurückgibt (manche Setups senden bereits sinnvolles HTML) und welche Crawler Sie tatsächlich zufriedenstellen müssen (Google rendert JS ziemlich zuverlässig; Bing und die meisten KI-Crawler weniger – siehe unten). CSR, das diese Prüfungen bereits besteht, muss nicht zu SSR werden, nur weil die Website eine SPA ist.

Serverseitiges Rendering (SSR)

Der Server führt Ihre App für jede Anfrage aus und gibt vollständig geformtes HTML für diese Route zurück, dann „hydratisiert“ der Client es in eine lebende SPA. Dies ist die robusteste Option, weil der Crawler beim ersten Abruf vollständigen Inhalt erhält, ohne dass JS-Ausführung erforderlich ist.

Statisches Vorrendern / SSG

Statt pro Anfrage zu rendern, erstellen Sie das HTML jeder Route im Voraus beim Deployment. Perfekt für Inhalte, die sich nicht pro Benutzer ändern. Aus meinem eigenen JavaScript-SEO-Schreiben: Jede Art von SSR, statischem Rendering und Vorrendern-Setup ist für Suchmaschinen in Ordnung – was Sie vermeiden sollten, ist, Inhalte hinter rein clientseitigem Rendering einzuschließen.

Oder: Nicht selbst zusammenbauen – ein Meta-Framework verwenden

Für einen Neubau ist die ehrliche Empfehlung, kein handgebautes clientseitiges Routing ausschließlich mit React Router oder Vue Router zu verwenden. Ein Meta-Framework – Next.js, Nuxt, Angular mit seinem SSR-Paket, SvelteKit oder Remix – bietet SSR/SSG und routenbasierte Metadaten direkt mit, wodurch diese gesamte Problemkategorie umgangen wird. (Jedes davon hat auf dieser Website einen eigenen ausführlichen Leitfaden.) Seien Sie ehrlich über die Kehrseite: Die Nachrüstung von SSR auf eine bestehende handgebaute SPA ist echte Entwicklungsarbeit – ein Migrationsprojekt, kein Konfigurationsschalter.

Dynamisches Rendering als Übergangslösung, nicht als Ziel

Sie können Crawlern einen separat gerenderten HTML-Snapshot (dynamisches Rendering) ausliefern. Sowohl Google als auch Bing akzeptieren das – Bing “recommend[s] dynamic rendering as a great alternative for websites relying heavily on JavaScript” (Übersetzung) „empfiehlt dynamisches Rendering als hervorragende Alternative für Websites, die stark auf JavaScript angewiesen sind“ (weitergegeben aus Bings Blog von 2018; die beiden kurzen Zitate zu den Fähigkeiten von bingbot unten sind direkt verifiziert, dieses längere ist nicht unabhängig überprüft) – aber Google stellt klar, dass es eine Problemumgehung ist: “Dynamic rendering was a workaround and not a long-term solution… Instead, we recommend that you use server-side rendering, static rendering, or hydration as a solution.” (Übersetzung) „Dynamisches Rendering war eine Problemumgehung und keine langfristige Lösung … Stattdessen empfehlen wir serverseitiges Rendering, statisches Rendering oder Hydration als Lösung.“ (Weitergegeben aus Googles Dokumentation zum dynamischen Rendering; der Wortlaut stimmt mit dem überein, was bereits im umfassenderen JavaScript-SEO-Leitfaden dieser Website zitiert wird.) Es ist eine Brücke, keine Architektur.

History API vs. Hash-Routing (#!)

Fünf Zustände, nicht zwei

Das Testen einer SPA-Route wird verwirrend, weil „funktioniert es“ tatsächlich fünf verschiedene, separat überprüfbare Zustände umfasst:

ZustandWas es ist
ServerantwortDie Bytes, die eine frische, JS-freie HTTP-Anfrage an eine URL tatsächlich zurückbekommt.
Gerendertes DOMWas ein Browser (oder der Renderer von Googlebot) aufbaut, nachdem er JavaScript gegen diese Serverantwort ausgeführt hat.
SuchverarbeitungWie Google die Serverantwort separat crawlt, die Seite später rendert und auf Basis von beidem indexiert.
Vollständige Browser-NavigationEine tatsächliche neue HTTP-Anfrage an eine URL – der einzige Zustand, der die Serverantwort oder den Statuscode ändern kann.
Soft-Navigation im BrowserEin History-API-Übergang (pushState/replaceState), der die sichtbare URL, den Browserverlauf und die On-Screen-Benutzeroberfläche ändert.

Der Punkt, den alle verwechseln: Eine Soft-Navigation ändert die URL und die Benutzeroberfläche, erzeugt aber nicht von sich aus eine neue HTTP-Antwort oder einen neuen Status – das passiert nur bei einer vollständigen Navigation (oder einer gleichwertigen direkten Anfrage, wie curl). Das Testen einer Route durch Durchklicken der App von der Startseite aus übt die Soft-Navigation; das Testen durch direkte Anfrage der URL übt die Serverantwort. Beides ist wichtig, und beides kann unterschiedlich ausfallen.

Was Ihnen die History API bietet

Die Empfehlung von Google ist eindeutig: “We recommend using the History API to load different content based on the URL in a SPA.” (Übersetzung) „Wir empfehlen, in einer SPA die History API zu verwenden, um abhängig von der URL unterschiedliche Inhalte zu laden.“ (Im Live-Dokument ist „History API“ ein Link, daher zielt dieser Deep Link auf den Einleitungssatz.) Die History API (pushState/replaceState) ermöglicht es Ihrem Router, die sichtbare URL in einen echten, mit Lesezeichen versehbaren Pfad zu ändern – /products, nicht /#/products – ohne vollständiges Neuladen. Das behebt die Adressierbarkeits-Hälfte: Jede Ansicht hat jetzt eine URL, auf die ein Server anders antworten könnte.

Warum Fragment-/Hash-URLs für den Server – und für Google – unsichtbar sind

Da das Fragment den Server nie erreicht (siehe oben), ist die History API der einzige Weg, jeder Route eine URL zu geben, die der Server tatsächlich ausliefern kann. Google legt in seinem Grundlagendokument eine Untergrenze fest: “don’t use fragments to load different page content. The following example is a bad practice, because Googlebot can’t reliably resolve the URLs.” (Übersetzung) „Verwenden Sie keine Fragmente, um unterschiedliche Seiteninhalte zu laden. Das folgende Beispiel ist ungeeignet, weil Googlebot die URLs nicht zuverlässig auflösen kann.“ Dies ist dieselbe Anleitung, die ich auch anderswo geschrieben habe – verwenden Sie normal aussehende URLs wie /products, nicht Hash-URLs wie /#/products, weil Google die Hash-URLs nicht zuverlässig indexieren kann.

Die Einstellung von 2015, kurz

Die Hash-Bang-Ära (#!) endete mit Googles Beitrag von 2015: “Times have changed. Today, as long as you’re not blocking Googlebot from crawling your JavaScript or CSS files, we are generally able to render and understand your web pages like modern browsers.” (Übersetzung) „Die Zeiten haben sich geändert. Solange Googlebot nicht am Crawlen Ihrer JavaScript- oder CSS-Dateien gehindert wird, können wir Ihre Webseiten heute im Allgemeinen wie moderne Browser rendern und verstehen.“ Außerdem heißt es: “you can use the History API pushState() to ensure accessibility for a wider range of browsers (and our systems).” (Übersetzung) „Sie können pushState() aus der History API verwenden, um die Zugänglichkeit für eine größere Bandbreite von Browsern (und unsere Systeme) sicherzustellen.“ Eine Nuance, die man behalten sollte: Google hat alte Hash-Bang-Seiten nicht sofort deindexiert — “we’ll generally crawl, render, and index the #! URLs” (Übersetzung) „Wir werden die #!-URLs im Allgemeinen crawlen, rendern und indexieren“ — aber “we can still try” (Übersetzung) „Wir können es weiterhin versuchen“ bedeutet nicht “you should still do this” (Übersetzung) „Sie sollten dies weiterhin tun“.

Jede Route unabhängig indexierbar machen

Saubere URLs und echtes HTML sorgen dafür, dass Sie gecrawlt werden. Um korrekt indexiert zu werden, braucht jede Route ihre eigenen Signale.

Canonical-Tags pro Route – und die Falle der „restriktivsten Direktive“

Jede Route braucht ihr eigenes rel=canonical im gerenderten DOM. Die Falle: Wenn ein Platzhalter-Canonical (oder ein noindex) im rohen HTML-Grundgerüst ausgeliefert wird und JavaScript es später überschreiben soll, kann es zu einem Konflikt kommen. Google löst Konflikte zwischen der rohen und der gerenderten Version, indem es das restriktivere Signal übernimmt – wie ich es schon früher formuliert habe: Google wählt die restriktivsten Aussagen zwischen dem HTML und der gerenderten Version einer Seite. Ein versehentliches noindex oder ein falsches Canonical im Grundgerüst kann die gesamte Route stillschweigend unterdrücken, selbst nachdem Ihr JS es „korrigiert“ hat.

Titel und Meta-Descriptions pro Route per JavaScript

Diese per JS zu setzen ist in Ordnung – Google sagt das direkt: “You can use JavaScript to set or change the meta description as well as the <title> element.” (Übersetzung) „Sie können JavaScript verwenden, um sowohl die Meta-Description als auch das <title>-Element festzulegen oder zu ändern.“ Die Anforderung ist, dass sie im gerenderten DOM landen, das Google auswertet, und nicht nur kurz aufblitzen. Geben Sie jeder Seite ihren eigenen Titel und ihre eigene Beschreibung, die sich bei einem Routenwechsel aktualisieren.

Machen Sie Ihre Links zwischen Routen zu echten Ankern mit href-Attributen, nicht zu Klick-Handlern auf <div>s. Google entdeckt URLs, indem es hrefs extrahiert; ein <div onClick>, das über den Router navigiert, ist für die Link-Extraktion des Crawlers unsichtbar. Und verlassen Sie sich nicht auf clientseitigen Zustand, um Inhalte über diese Navigationen zu transportieren – der Renderer von Google “does not retain state across page loads: Local Storage and Session Storage data are cleared across page loads. HTTP Cookies are cleared across page loads.” (Übersetzung) „behält keinen Zustand über Seitenladevorgänge hinweg: Daten im Local Storage und Session Storage werden zwischen Seitenladevorgängen gelöscht. HTTP-Cookies werden zwischen Seitenladevorgängen gelöscht.“

Sitemap-Generierung für SPA-Routen

Es gibt kein spezielles „SPA-Sitemap“-Format – es ist ein Abrechnungsproblem

Das Sitemap-Protokoll ist für SPAs unverändert. Die eigentliche Arbeit besteht darin, sicherzustellen, dass jede Route, die Sie separat auflisten, echtes, eindeutiges, gerendertes HTML ergibt. Eine Sitemap mit 500 clientseitigen Routen ist wertlos, wenn alle diese Routen dasselbe Grundgerüst zurückgeben. Die Sitemap ist also eine Folge Ihrer Rendering-Strategie, kein Ersatz dafür.

Umgang mit dynamischen/parametrisierten Routen (/product/:id)

Bei Apps mit parametrisierten Routen können Sie die Liste nicht manuell pflegen. Der Sitemap- Generator muss gegen dieselbe Datenquelle laufen, die auch die App verwendet – ein Build-Skript oder ein Server-Endpunkt, der jede id aufzählt – damit Sitemap und App nie auseinanderdriften. Diese aufgezählten URLs sind nur dann eine Auflistung wert, wenn sie per SSR oder Prerendering gerendert wurden.

Die Sitemap mit dem synchron halten, was serverauflösbar ist

Generieren Sie die Sitemap als Teil Ihres Builds neu oder in einem Zeitplan, der an Ihre Inhaltsquelle gekoppelt ist. Eine Route, die 404 liefert (oder schlimmer, Soft-404 bei 200), aber in Ihrer Sitemap steht, ist ein Crawl-Budget- und Qualitätssignal, das Sie nicht wollen.

Testen, was Google tatsächlich sieht

Überprüfen Sie nicht nur eine URL stichprobenartig. Der ganze Sinn des SPA-Problems ist, dass sich Routen im Browser unterscheiden können, aber nicht auf der Leitung, also testen Sie mehrere Routen auf der Ebene des rohen HTML:

  • Rufen Sie das rohe HTML pro Route ab mit curl (bei Bedarf mit einem Googlebot-User-Agent) und bestätigen Sie, dass der Inhalt pro URL eindeutig ist, nicht die gemeinsame Hülle.
  • URL Inspection in der Search Console – vergleichen Sie das gecrawlte/gerenderte HTML für mehrere Routen und bestätigen Sie, dass Titel, Beschreibung und Canonical pro Route vorhanden sind.
  • Bestätigen Sie, dass Fehlerrouten das richtige Signal zurückgeben – eine „Nicht gefunden“-Ansicht sollte entweder auf einen echten Fehlerstatus umleiten oder noindex im gerenderten DOM tragen.

Häufige Mythen über SPA-SEO

  • „Google kann SPAs überhaupt nicht indexieren.“ Veraltet. Google verwendet Evergreen-Chromium und rendert SPA-Inhalte im Allgemeinen. Die eigentlichen Risiken sind spezifisch: Soft-404s, Hash-Routing, Rendering-Timeouts und Nicht-Google-Crawler (Bing weniger zuverlässig, die meisten KI-Crawler gar nicht).
  • „Die History-API zu verwenden, behebt SPA-SEO.“ Sie behebt nur die Adressierbarkeit. Wenn der Server weiterhin für jede Route dieselbe Hülle zurückgibt, muss Google weiterhin JS ausführen, um etwas zu sehen.
  • „Hash-Routing funktioniert weiterhin als Fallback.“ Seit 2015 veraltet und seitdem davon abgeraten.
  • „Client-seitiges Rendering ist eine Ranking-Strafe.“ Es gibt keine direkte CSR-Strafe. Der Schaden ist indirekt – fehlgeschlagenes/verzögertes Rendering, Soft-404s und doppelte Clusterbildung durch Rendering-Timeouts reduzieren, was indexiert wird.
  • „Pre-Rendering für Bots ist Cloaking.“ Nicht, wenn der Inhalt dem entspricht, was Benutzer letztendlich sehen – nur das Wann/Wo des Renderings unterscheidet sich, nicht der Inhalt.
  • „Sie benötigen einen speziellen SPA-Sitemap-Generator.“ Es gibt kein spezielles Format; die Arbeit besteht darin, jede aufgeführte Route in echtes HTML aufzulösen.

Häufig gestellte Fragen

Kann Google eine Single-Page-Anwendung indexieren? Ja, wenn jede Route in echtes, eindeutiges HTML (per SSR/Prerendering) unter einer echten URL aufgelöst wird. Reine Client-only-SPAs tun das oft nicht.

Brauche ich SSR, oder ist Client-seitiges Rendering jemals in Ordnung? CSR kann für Inhalte funktionieren, die nicht ranken müssen, aber für alles, was Sie zuverlässig indexiert haben möchten, prerendern oder SSR verwenden – verlassen Sie sich nicht darauf, dass der Renderer Ihr JS rechtzeitig ausführt.

Ist Hash-Routing (#!) schlecht für SEO? Ja – das Fragment erreicht den Server nie, daher kann Google diese URLs nicht zuverlässig auflösen. Verwenden Sie die History-API.

Braucht jede Route ihr eigenes Canonical-Tag? Ja, im gerenderten DOM – und stellen Sie sicher, dass nichts Restriktiveres (ein versehentliches noindex oder falsches Canonical) in der rohen Hülle ausgeliefert wird.

Ist ein Prerendering-Dienst Cloaking? Nein, vorausgesetzt, der Bots ausgelieferte Inhalt entspricht dem, was Benutzer sehen.

Sollte ich ein Meta-Framework verwenden, anstatt das Routing selbst zu bauen? Für einen neuen Build, normalerweise ja – Next.js/Nuxt/Angular-SSR/SvelteKit bieten Ihnen SSR/SSG und Metadaten pro Route kostenlos.

Warum gibt meine SPA 200 für Seiten zurück, die nicht existieren? Weil der Client-seitige Router den ursprünglichen 200-Status für virtuelle Navigationen beibehält. Beheben Sie es mit einer JS-Umleitung zu einem echten Fehlerstatus oder einem gerenderten noindex.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.