<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Warum mein Angular-blog nicht von Google indiziert wird (und wie ich das Problem behoben habe)

Einleitung: Wenn Google Ihr Blog einfach ignoriert

Vor ein paar Wochen wurde ich auf ein ärgerliches Problem aufmerksam: Die auf demselben Blog veröffentlichten Artikel erschienen nicht bei Google. Sie waren nicht schlecht positioniert, sie bekamen nicht wenig Verkehr – sie waren einfach nicht indexiert. Beim Öffnen der Google Search Console war die Situation klar: Von den 32 in der Sitemap gesendeten URLs waren nur 4 indexiert (Startseite, Blogeintrag, Projekte, Kontakte). Null Artikel.

Was folgt, ist der tatsächliche Weg, den ich eingeschlagen habe, um das Problem zu diagnostizieren und zu beheben, vom ersten technischen Verdacht bis zur versteckten Ursache, die niemand jemals überprüft: doppelter Inhalt. Wenn Sie einen technischen Blog betreiben, insbesondere zu einer Angular-Anwendung mit serverseitigem Rendering, werden Sie wahrscheinlich mindestens eines dieser Probleme erkennen.

Erster Verdacht: Seiten werden auf der Clientseite statt auf der Serverseite gerendert

Die erste Prüfung ist in diesen Fällen immer die gleiche: Was sieht der Googlebot wirklich, wenn er eine Seite besucht? Wenn bei einer Angular-App mit SSR das serverseitige Rendering nicht wie erwartet funktioniert, erhält der Crawler nur die leere HTML-Shell – keinen spezifischen , keine Meta-Beschreibung, keinen tatsächlichen Textinhalt, alles über JavaScript nach Angular-Bootstraps gefüllt.</p> <p>In meinem Fall lag die Ursache in angle.json: Das Projekt verwendete immer noch das alte boolesche Flag „prerender“: true anstelle des neueren „outputMode“: „server“. Mit dem booleschen Flag rendert Angular nur statische Routen vor und ignoriert getPrerenderParams() stillschweigend – die Funktion, die dynamische Routen wie /blog/:slug erweitert, indem sie alle vom Backend veröffentlichten Slugs abruft. Ergebnis: Alle statischen Seiten (Startseite, Projekte, Kontakte) wurden korrekt vorgerendert, aber jeder einzelne Blog-Beitrag blieb eine reine clientseitige Seite.</p> <pre><code>// angular.json — prima (sbagliato) "prerender": true // angular.json — dopo (corretto) "outputMode": "server"</code></pre> <p>Mit „outputMode: „server““ ruft Angular tatsächlich getPrerenderParams() für jede in app.routes.server.ts konfigurierte dynamische Route auf, die in meinem Fall GET /blog/posts im Backend abfragt und für jeden geposteten Slug eine statische Seite generiert. Nach dem Fix erzeugte der Build schließlich jeden Artikel als echtes HTML, wobei der Titel, die Meta-Beschreibung und der JSON-LD-Artikel im ersten Byte bereit standen.</p> <h2>Apaches versteckter Fehler: 301 Weiterleitungen auf jeder vorgerenderten Seite</h2> <p>Nachdem das Vorrendering behoben wurde, ergab eine Überprüfung mit einem einfachen Curl -I (kein Browser, der Weiterleitungen transparent folgt und das Problem verbirgt) einen zweiten Fehler: Jede vorgerenderte Seite antwortete mit 301 Permanent verschoben zur gleichen URL mit hinzugefügtem abschließenden Schrägstrich.</p> <p>Die Ursache ist das Standardverhalten von Apache, mod_dir: Wenn eine Anfrage mit einem echten Verzeichnis auf der Festplatte übereinstimmt (und das ist genau das, was durch das Vorrendern erstellt wird: /blog/article-name/index.html), leitet Apache automatisch um, indem es den abschließenden Schrägstrich hinzufügt, bevor die mod_rewrite-Regeln in der .htaccess eingreifen können. Der Inhalt hinter der Weiterleitung war korrekt, aber die auf jeder Seite deklarierte kanonische URL (ohne abschließenden Schrägstrich, konsistent mit der Sitemap) gab nie eine direkte 200 zurück – die Crawler erhielten immer eine andere URL als die als kanonisch markierte.</p> <pre><code><IfModule mod_dir.c> DirectorySlash Off </IfModule> RewriteCond %{REQUEST_FILENAME} -d RewriteCond %{REQUEST_FILENAME}/index.html -f RewriteRule ^(.*)$ $1/index.html [L]</code></pre> <p>Durch das Deaktivieren des automatischen Schrägstrichs und das Hinzufügen einer expliziten Regel, die index.html direkt auf der URL ohne Schrägstrich bereitstellt, begann jede vorgerenderte Seite mit 200 auf der genauen kanonischen URL zu antworten.</p> <h2>Statische Sitemap vs. Sitemap, die aus echtem Inhalt generiert wird</h2> <p>Ein drittes Problem, banaler, aber ebenso blockierend: Die Sitemap enthielt nur die Listing-/Blogseite, nicht die einzelnen Artikel. Jeder neu veröffentlichte Beitrag blieb für Google unsichtbar, bis er über interne Links entdeckt wurde – ein langsamer und keineswegs garantierter Prozess.</p> <p>Die Lösung bestand darin, das Sitemap-Generierungsskript von einer statischen Liste von Routen in eine Funktion umzuwandeln, die das Backend nach jedem veröffentlichten Beitrag abfragt:</p> <pre><code>async function fetchBlogRoutes() { const res = await fetch(`${API_BASE_URL}/blog/posts?page=1&limit=50`); const { data, meta } = await res.json(); const posts = [...data]; for (let page = 2; page <= meta.totalPages; page++) { const r = await fetch(`${API_BASE_URL}/blog/posts?page=${page}&limit=50`); const j = await r.json(); posts.push(...j.data); } return posts.map(p => ({ loc: `/blog/${p.slug}`, lastmod: p.updatedAt })); }</code></pre> <p>Das gleiche Skript läuft jetzt auch als GitHub Action, jede Nacht und bei jedem Push, sodass jeder neu veröffentlichte Artikel automatisch in die Sitemap gelangt, ohne dass ein manueller Eingriff erforderlich ist.</p> <h2>Die heimtückischste Ursache: Duplicate Content</h2> <p>Nach all diesen technischen Korrekturen wurde die Situation verbessert, aber nicht gelöst: Die meisten Artikel blieben im Status „Erkannt, derzeit nicht indexiert“ – Google kannte die URLs aus der Sitemap, hatte sie jedoch noch nicht als relevant genug für eine Indexierung angesehen. Als ich die Liste Artikel für Artikel analysierte, fand ich zwei Beiträge, die innerhalb von 28 Sekunden veröffentlicht wurden, zum exakt gleichen Thema, mit kaum unterscheidbaren Titeln und fast überlappenden Inhalten – wahrscheinlich ein versehentlicher Doppelbeitrag aus dem Editor-Panel.</p> <p>Duplicate Content blockiert nicht die Indexierung einer einzelnen Seite: Es ist ein Qualitätssignal, das auf der Ebene der gesamten Domain bewertet wird. Auf einer neuen Website mit sehr geringer externer Autorität können ein paar doppelte Beiträge ausreichen, um Google selbst vor völlig originellen Inhalten auf derselben Domain misstrauischer zu machen. Ich habe die Version mit weniger Aufrufen gelöscht und die Sitemap neu generiert: Dies ist wahrscheinlich der einzelne Eingriff mit den größten Auswirkungen auf die Gesamtindizierung der Site.</p> <h2>So lesen Sie den Search Console-Bericht „Seitenindizierung“ tatsächlich</h2> <p>Der Google Search Console-Bericht gruppiert nicht indizierte Seiten nach Grund und jedes Label erfordert eine andere Aktion:</p> <ul> <li>Erkannt, derzeit nicht indiziert: Google kennt die URL (normalerweise aus der Sitemap), hat sie aber noch nicht gecrawlt. Es ist eine Frage der Zeit und des Crawling-Budgets und kein zu behebender Fehler.</li> <li>Gecrawlt, derzeit nicht indiziert: Google hat die Seite besucht, sich jedoch dafür entschieden, sie nicht zu indizieren, häufig aufgrund von Inhalten, die als minderwertig oder doppelt eingestuft wurden.</li> <li>Alternative Seite mit entsprechendem kanonischen Tag: normal für übersetzte Versionen desselben Inhalts, die korrekt auf die Hauptversion verweisen – kein Fehler.</li> <li>Umleitungsfehler: Achten Sie auf das Datum des letzten Scans. Wenn der Fehler nach diesem Datum behoben wurde, zeigt der Bericht lediglich veraltete Daten an: Überprüfen Sie einfach mit „curl -I“, ob die URL heute 200 antwortet, und erzwingen Sie dann mit „Fix validieren“ eine neue Überprüfung.</li> </ul> <h2>Praktische Checkliste</h2> <ul> <li>Überprüfen Sie mit curl -I (nicht mit dem Browser), dass jede Seite direkt auf die kanonische URL reagiert, ohne versteckte Weiterleitungen.</li> <li>Überprüfen Sie, ob der von einem Crawler empfangene Inhalt mit dem eines echten Browsers identisch ist: curl -A „Googlebot“ muss vollständiges HTML zurückgeben, keine leere Shell, die auf JavaScript wartet.</li> <li>Generieren Sie die Sitemap dynamisch aus veröffentlichten Inhalten, nicht aus einer statischen Routenliste.</li> <li>Suchen Sie in Ihren Beiträgen nach nahezu identischen Titeln oder Themen – doppelter Inhalt ist ein Domain-Problem, nicht das Problem einer einzelnen Seite.</li> <li>Verwenden Sie „Request Indexing“ in der Search Console für neuere Beiträge, anstatt auf den natürlichen Crawl zu warten, der bei einer jungen Domain Wochen dauern kann.</li> </ul> <h2>Realistische Zeitpläne</h2> <p>Bei einer neuen Domain mit wenigen Anzeichen externer Autorität ist es normal, dass die erste Indizierung einige Tage (bei manueller Anfrage) bis mehrere Wochen (bei natürlichem Crawl) dauert. Was wirklich zählt, ist die Beseitigung technischer und inhaltlicher Qualitätshindernisse: Sobald sie beseitigt sind, müssen Sie Google nur noch Zeit geben, die Domain zu überprüfen und ihr zu vertrauen.</p> </article>

💬 Leser-Notizen

0 Notizen

Notiz schreiben

Teile deine Meinung, einen Vorschlag oder ein Kompliment

Neueste Notizen

Noch keine Notizen. Sei der Erste, der kommentiert!