Erscheinungsbild
0006 — Indexierung aus Routen-Policy und Umgebung
Status: angenommen · Milestone: M4
Kontext
Ob eine Seite in den Google-Index gehört, hängt an zwei unabhängigen Fragen: an der Seite selbst (eine Fehlerseite gehört nie hinein) und an der Ausgabe (ein Preview-Stand gehört nie hinein).
Beide Fragen wurden im Referenzprojekt an derselben Stelle beantwortet — nämlich gar nicht. Der Preview-Stand lief unter einer eigenen Subdomain ohne Sperre, und die Sitemap entstand aus einer Liste, die jemand von Hand pflegte. Sie enthielt zwei Seiten, die es nicht mehr gab, und keine der drei, die dazugekommen waren.
Ein versehentlich indexierter Preview-Stand ist teuer: Er konkurriert mit der echten Website um dieselben Begriffe und lässt sich nur langsam wieder aus dem Index entfernen. Der umgekehrte Fehler — die Produktion bleibt versehentlich gesperrt — ist ebenfalls schlimm, fällt aber innerhalb von Tagen auf.
Entscheidung
Zwei getrennte Funktionen in src/lib/seo/indexing.ts beantworten die zwei Fragen:
ts
isIndexableRoute('/styleguide'); // false — gilt in jeder Umgebung
isIndexableEnvironment('preview'); // false — gilt für jede SeiteNICHT_INDEXIERBARE_ROUTEN ist die einzige Quelle dafür, welche Pfade draußen bleiben. Sie speist gleichzeitig das robots-Meta, robots.txt und die Sitemap. Eine Seite kann dadurch nicht mehr in der Sitemap stehen und zugleich noindex tragen.
Indexierbar ist ausschließlich PUBLIC_ENVIRONMENT=production. Der Vorgabewert der Variablen ist development — ein Build, bei dem sie vergessen wurde, ist nicht indexierbar.
robots.txt ist ein Endpunkt (src/pages/robots.txt.ts), keine Datei unter public/: Sein Inhalt hängt von Umgebung und Feature-Flag ab.
Die Sitemap entsteht in einer eigenen Integration im Hook astro:build:done — ohne zusätzliche Abhängigkeit und ohne lastmod.
Begründung
Getrennte Funktionen statt eines Flags. Die Sitemap beschreibt den Aufbau der Website. Sie soll im Preview-Build dieselbe sein wie in der Produktion, damit ein Vergleich der beiden Ausgaben etwas aussagt. Verboten wird das Indexieren über robots.txt und das Meta-Tag.
Die Liste statt eines Props an der Seite. Ein noindex als Prop wäre ein Schalter, den man beim Anlegen einer Seite vergisst — und den die Sitemap ohnehin nicht sehen könnte, weil sie nach dem Rendern entsteht. Das Prop bleibt für Einzelfälle erhalten (etwa eine Danke-Seite), ist aber nicht der Regelweg.
Integration statt Endpunkt für die Sitemap. Nur der Build kennt die vollständige Liste der erzeugten Seiten. Ein Endpunkt unter src/pages/ müsste sie sich aus Dateinamen zusammensuchen und würde alles übersehen, was aus dynamischen Routen entsteht — stillschweigend.
Kein lastmod. Ein Datum, das bei jedem Build auf „heute" springt, ist eine Angabe über den Build und nicht über den Inhalt. Suchmaschinen entwerten es zu Recht. Sobald es mit den Inhalten eine echte Quelle für Änderungsdaten gibt, gehört es hinein.
Verworfene Alternativen
@astrojs/sitemap. Die offizielle Integration kann mehr, als hier gebraucht wird, und bringt drei weitere Pakete mit. Die Auswahl der Seiten — der einzige interessante Teil — müsste trotzdem über eine eigene Filterfunktion laufen. Der Rest ist ein festes XML-Gerüst von zwanzig Zeilen.
robots.txt als statische Datei in public/. Wäre in jeder Umgebung dieselbe und damit in genau der einen falsch, in der es darauf ankommt.
Indexierung auch in preview erlauben, gesteuert über einen Schalter. Ein Schalter, den man falsch stellen kann, an genau der Stelle, an der ein Fehler am teuersten ist.
Konsequenzen
- Ein Produktions-Deployment muss
PUBLIC_ENVIRONMENT=productionsetzen. Ohne die Variable ist die Website vollständig gesperrt. Das Deployment (M8) setzt sie in der CI. - Die Fixture „full" wird als Produktionsausgabe gebaut (
pnpm run build:fixture:full), die Fixture „minimal" als Entwicklungsstand. Nur so sind beide Umgebungszustände auf dem ausgelieferten HTML geprüft. - Deshalb trägt die Fixture „full" eine
site.urlohne Platzhalter-Host: Inproductionbricht die Umgebungsprüfung beiexample.deab..testist für solche Zwecke reserviert (RFC 6761). - Neue Seiten, die nicht in den Index gehören, gehören in
NICHT_INDEXIERBARE_ROUTEN— nicht alsnoindex-Prop an die Seite.