L'App Router génère les deux à partir de code typé : fini le XML statique périmé :
// 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",
};
}
Des détails qui comptent :
lastModifieddoit être la vraie date de mise à jour du contenu, et nonnew Date()à chaque build : mentir sur la fraîcheur apprend à Google à ignorer ce champ.- Plus de 50 000 URL ? Utilisez
generateSitemaps()pour découper en plusieurs fichiers. - Ne bloquez pas le CSS/JS dans robots.txt : Google en a besoin pour rendre la page.
- Un Disallow dans
robots.txtne retire pas les pages de l'index : il arrête seulement l'exploration. Pour qu'une page n'apparaisse pas dans les résultats, utiliseznoindexvia les métadonnées (robots: { index: false }) et ne la bloquez pas dans robots.txt (une page bloquée ne peut pas être lue, donc personne ne verra le noindex).
Checklist :
-
sitemap.tsne contient que des adresses canoniques, indexables, au statut 200 -
lastModifiedreflète de vrais changements - Sitemap soumis dans Google Search Console et Bing Webmaster Tools
- noindex vs robots.txt utilisés correctement (ils règlent des problèmes différents)
