robots.txt sperrte /checkout, /warenkorb, /konto und /rechnung fuer Crawler. Das verbietet aber nur das Abrufen, nicht das Indexieren: verweist irgendwo ein Link auf eine dieser Seiten, nimmt Google die URL trotzdem auf — als inhaltslosen Treffer mit dem Hinweis "Fuer diese Seite sind keine Informationen verfuegbar". Der uebliche Ausweg, ein noindex-Meta-Tag, half hier nicht: eine per robots.txt gesperrte Seite wird nie geladen, also sieht der Crawler das Tag nie. Die Sperre verhinderte ausgerechnet das Mittel, das die URLs wieder aus dem Index geholt haette. Keine dieser Seiten trug bisher ueberhaupt ein noindex. Jetzt greifen beide Mechanismen zusammen statt gegeneinander: - lib/seo.ts buendelt die beiden robots-Bausteine. noIndex laesst Links weiterhin folgen (Footer- und Produktlinks im Warenkorb sollen ihre Linkkraft behalten), noIndexPrivate unterbindet zusaetzlich Cache-Kopie, Bildindexierung und Textausschnitt. - Kasse, Bestellbestaetigung und Rechnungsansicht bekommen noIndexPrivate, der Warenkorb noIndex. - Der gesamte Kontobereich bekommt das Tag einmal ueber ein neues konto/layout.tsx statt in zehn Unterseiten einzeln. Metadata erbt feldweise, ein eigener title in einer Unterseite loescht den Wert also nicht. - robots.txt sperrt nur noch /api/ und /admin — also das, was ein Crawler wirklich nie abrufen soll. Alles andere darf gecrawlt werden, damit das noindex ueberhaupt gelesen wird. /uploads bleibt bewusst offen: dort liegen die Produktbilder, die in der Bildersuche erwuenscht sind. Zusaetzlich eine ausdrueckliche Regel fuer GPTBot, ClaudeBot, PerplexityBot und Google-Extended (oeffentliche Inhalte ja, interne Bereiche nein) sowie die host-Angabe in robots.txt. tsc: 0 Fehler. vitest: 359/359 gruen. eslint: 0 Warnungen. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
54 lines
2.3 KiB
TypeScript
54 lines
2.3 KiB
TypeScript
import type { MetadataRoute } from "next";
|
|
import { getSiteUrl } from "@/lib/url";
|
|
|
|
// Muss zur Laufzeit statt zur Build-Zeit gerendert werden (gleicher Grund wie
|
|
// bei sitemap.ts): sonst backt Next.js die Datei beim Build als statische
|
|
// Antwort ein — samt dem Wert, den NEXT_PUBLIC_SITE_URL GERADE DA hatte. Im
|
|
// Docker-Build ist die Variable nicht gesetzt (siehe Dockerfile), die Sitemap-
|
|
// Zeile fror damit dauerhaft auf dem Rueckfallwert aus site-config.ts ein,
|
|
// egal unter welcher Domain die Seite spaeter tatsaechlich lief.
|
|
export const dynamic = "force-dynamic";
|
|
|
|
// Nur Pfade, die ein Crawler ueberhaupt nicht ABRUFEN soll. Das ist eine
|
|
// bewusst kurze Liste, denn "Disallow" und "noindex" schliessen einander aus:
|
|
// eine per robots.txt gesperrte Seite wird nie geladen, also sieht Google das
|
|
// dort gesetzte noindex nie — und nimmt die URL bei einem eingehenden Link
|
|
// trotzdem als inhaltslosen Treffer in den Index auf.
|
|
//
|
|
// Deshalb stehen /checkout, /warenkorb, /konto und /rechnung hier NICHT mehr
|
|
// (frueher taten sie es). Sie tragen stattdessen ein noindex-Meta-Tag (siehe
|
|
// lib/seo.ts) und duerfen dafuer gecrawlt werden — nur so faellt eine bereits
|
|
// indexierte URL wieder aus dem Index. Der zusaetzliche Crawl-Aufwand ist bei
|
|
// einer Handvoll Seiten vernachlaessigbar.
|
|
//
|
|
// Was bleibt:
|
|
// - /api: Maschinenschnittstellen ohne jeden Nutzen in Suchergebnissen.
|
|
// - /admin: liefert einem anonymen Crawler ohnehin nur eine Weiterleitung auf
|
|
// die Anmeldung; jeder Abruf ist reine Last.
|
|
// - /uploads ist bewusst NICHT gesperrt — dort liegen die Produktbilder, die
|
|
// in der Google-Bildersuche durchaus erwuenscht sind.
|
|
const disallow = ["/api/", "/admin"];
|
|
|
|
export default function robots(): MetadataRoute.Robots {
|
|
return {
|
|
rules: [
|
|
{
|
|
userAgent: "*",
|
|
allow: "/",
|
|
disallow,
|
|
},
|
|
// GPTBot & Co. duerfen die oeffentlichen Inhalte lesen (Sichtbarkeit in
|
|
// KI-Antworten ist erwuenscht), aber ohne die internen Bereiche.
|
|
// Separat aufgefuehrt, damit eine spaetere Entscheidung dagegen nur
|
|
// diesen Block betrifft und nicht die Suchmaschinen mit einschliesst.
|
|
{
|
|
userAgent: ["GPTBot", "ClaudeBot", "PerplexityBot", "Google-Extended"],
|
|
allow: "/",
|
|
disallow,
|
|
},
|
|
],
|
|
sitemap: `${getSiteUrl()}/sitemap.xml`,
|
|
host: getSiteUrl(),
|
|
};
|
|
}
|