Der App Router erzeugt beides aus typisiertem Code – Schluss mit veraltetem statischem XML:
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getPosts();
const postEntries = posts.map((post) => ({
url: `https://example.com/en/blog/${post.slug}`,
lastModified: post.updatedAt,
alternates: {
languages: {
pl: `https://example.com/pl/blog/${post.plSlug}`,
ru: `https://example.com/ru/blog/${post.ruSlug}`,
},
},
}));
return [{ url: "https://example.com", priority: 1 }, ...postEntries];
}
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: [{ userAgent: "*", allow: "/", disallow: ["/api/", "/admin/"] }],
sitemap: "https://example.com/sitemap.xml",
};
}
Details, auf die es ankommt:
lastModifiedsollte das echte Datum der Inhaltsaktualisierung sein, nichtnew Date()bei jedem Build – wer bei der Aktualität lügt, bringt Google bei, dieses Feld zu ignorieren.- Mehr als 50.000 URLs? Teilen Sie mit
generateSitemaps()auf mehrere Dateien auf. - Sperren Sie CSS/JS nicht in der robots.txt – Google braucht sie, um die Seite zu rendern.
- Ein Disallow in der
robots.txtentfernt Seiten nicht aus dem Index – es stoppt nur das Crawlen. Damit eine Seite nicht in den Ergebnissen landet, nutzen Sienoindexüber die Metadaten (robots: { index: false }) und sperren Sie sie nicht in der robots.txt (eine gesperrte Seite lässt sich nicht lesen, also sieht niemand das noindex).
Checkliste:
-
sitemap.tsenthält nur kanonische, indexierbare Adressen mit Status 200 -
lastModifiedspiegelt echte Änderungen wider - Sitemap in der Google Search Console und in den Bing Webmaster Tools eingereicht
- noindex vs. robots.txt richtig eingesetzt (sie lösen unterschiedliche Probleme)
