Seo

网站地图

Nebutra-Sailor 中 XML 网站地图的生成方式、如何包含来自 Sanity 的动态博客文章,以及如何向 Google Search Console 提交。

网站地图的工作原理

网站地图通过 Next.js App Router 文件系统约定生成。位于 app 目录根部的 sitemap.ts 文件导出一个异步函数,该函数返回一个 URL 对象数组。Next.js 将其转换为有效的 XML 网站地图,在 /sitemap.xml 提供服务。

文件位置:

apps/landing/src/app/sitemap.ts

静态网站地图

对于只有静态页面的站点,导出一个 URL 对象数组:

import type { MetadataRoute } from "next";

const BASE_URL = "https://nebutra.com";
const LOCALES = ["en", "zh", "ja", "ko", "de", "fr", "es"];

export default function sitemap(): MetadataRoute.Sitemap {
  const staticPages = [
    "",
    "/pricing",
    "/changelog",
    "/about",
    "/contact",
    "/blog",
  ];

  const entries: MetadataRoute.Sitemap = [];

  for (const locale of LOCALES) {
    for (const path of staticPages) {
      entries.push({
        url: `${BASE_URL}/${locale}${path}`,
        lastModified: new Date(),
        changeFrequency: path === "" ? "weekly" : "monthly",
        priority: path === "" ? 1.0 : 0.8,
      });
    }
  }

  return entries;
}

包含 Sanity 博客文章的动态网站地图

FEATURE_BLOG=true 时,从 Sanity 获取博客文章 slug 并添加到网站地图:

import type { MetadataRoute } from "next";
import { getAllPostSlugs } from "@/lib/sanity";

const BASE_URL = "https://nebutra.com";
const LOCALES = ["en", "zh", "ja", "ko", "de", "fr", "es"];

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const staticPages = ["", "/pricing", "/changelog", "/about", "/blog"];

  const staticEntries: MetadataRoute.Sitemap = LOCALES.flatMap((locale) =>
    staticPages.map((path) => ({
      url: `${BASE_URL}/${locale}${path}`,
      lastModified: new Date(),
      changeFrequency: "weekly" as const,
      priority: path === "" ? 1.0 : 0.7,
    }))
  );

  // 来自 Sanity 的动态博客文章
  const posts = await getAllPostSlugs(); // 返回 [{ slug, updatedAt, locale }]
  const blogEntries: MetadataRoute.Sitemap = posts.map((post) => ({
    url: `${BASE_URL}/${post.locale}/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: "monthly" as const,
    priority: 0.6,
  }));

  return [...staticEntries, ...blogEntries];
}

仅在需要获取动态数据时才将 sitemap() 设为 async。对于纯静态站点,同步函数可以避免不必要的异步开销。

robots.txt

robots.ts 文件控制爬虫可以访问哪些路径。它独立于网站地图,但通常会引用它:

import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: "*",
        allow: "/",
        disallow: ["/api/", "/_next/", "/studio/"],
      },
    ],
    sitemap: "https://nebutra.com/sitemap.xml",
  };
}

切勿在网站地图中包含已认证的应用路由(如 app.nebutra.com/dashboard)。只有公开落地页(nebutra.com)应该被索引。apps/web 应用应有自己的 robots.ts,拒绝所有爬虫访问。

在本地验证网站地图

pnpm --filter @nebutra/landing dev
# 访问:http://localhost:3002/sitemap.xml
# 访问:http://localhost:3002/robots.txt

在开发环境中,网站地图按需渲染;在生产环境中,网站地图会在 CDN 边缘缓存。

向 Google Search Console 提交

Google Search Console 中,添加您的资源(https://nebutra.com),并通过 DNS TXT 记录或 HTML 文件方式验证所有权。

在 Search Console 中,点击左侧边栏的 Sitemaps。在 URL 字段中输入 sitemap.xml,然后点击 Submit

提交后,Search Console 会显示已发现和已索引的 URL 数量。新网站地图通常需要 24–72 小时才能被处理。

Google 会自动重新爬取网站地图,但在重大版本发布后(新增语言、大量新博客文章),手动重新提交可以加快重新索引速度。

网站地图大小限制

Google 支持最多 50,000 个 URL 或 50 MB(未压缩)的网站地图。若超出此限制:

  1. 将其拆分为多个网站地图(如 sitemap-static.xmlsitemap-blog.xml)。
  2. /sitemap.xml 创建引用子网站地图的网站地图索引文件。

当需要网站地图索引时,使用 Next.js 内置的 generateSitemaps() 导出,将路由拆分到多个 sitemap.ts 文件:

// app/sitemap.ts  ← 成为 /sitemap.xml 的索引
import type { MetadataRoute } from "next";

export default function sitemap(): MetadataRoute.Sitemap {
  // 仅包含静态和短生命周期页面
  return [
    { url: "https://nebutra.com", lastModified: new Date() },
    { url: "https://nebutra.com/pricing", lastModified: new Date() },
  ];
}
// app/blog/sitemap.ts  ← /blog/sitemap.xml
import type { MetadataRoute } from "next";
import { getAllPostSlugs } from "@/lib/blog";

export async function generateSitemaps() {
  // 每批最多 5,000 个 URL
  const slugs = await getAllPostSlugs();
  const batches = Math.ceil(slugs.length / 5000);
  return Array.from({ length: batches }, (_, i) => ({ id: i }));
}

export default async function sitemap({ id }: { id: number }): Promise<MetadataRoute.Sitemap> {
  const slugs = await getAllPostSlugs();
  const page = slugs.slice(id * 5000, (id + 1) * 5000);
  return page.map((slug) => ({
    url: `https://nebutra.com/blog/${slug}`,
    lastModified: new Date(),
  }));
}

Next.js 会自动在 /sitemap.xml 生成一个网站地图索引,引用 /blog/sitemap/0.xml/blog/sitemap/1.xml 等子地图。请在 Google Search Console 中注册索引 URL。

相关文档

How is this guide?

目录