App Router генерирует и то и другое из типизированного кода — никакого протухшего статического 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", lastModified: new Date(), 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",
};
}
Важные детали:
lastModifiedдолжен быть реальной датой обновления контента, а неnew Date()на каждом билде — враньё про свежесть приучает Google игнорировать это поле.- Больше 50 000 URL? Разбивайте на несколько файлов через
generateSitemaps(). - Не блокируйте CSS/JS в robots.txt — они нужны Google для рендеринга страницы.
- Disallow в
robots.txtне убирает страницы из индекса — он лишь запрещает обход. Чтобы страница не попадала в выдачу, используйтеnoindexчерез метаданные (robots: { index: false }) и не блокируйте её в robots.txt (заблокированную страницу нельзя прочитать, и noindex никто не увидит).
Чеклист:
- В
sitemap.ts— только канонические, индексируемые URL с кодом 200 -
lastModifiedотражает реальные правки - Sitemap отправлен в Google Search Console и Bing Webmaster Tools
- noindex и robots.txt используются по назначению (это разные задачи)