首页 / Next.js 16 入门教程 / 静态资源与 robots/sitemap

Next.js 16 入门教程

静态资源与 robots/sitemap

本教程共 42 篇 · 第 20 篇 · 更新于 2026-07-30 · 约 6 分钟阅读

Next.jsNext.js 16 入门教程robots.txtsitemapSEO文件元数据

20. 静态资源与 robots/sitemap

本节目标:学会使用 Next.js 文件元数据 API 生成 robots.txt 和 sitemap.xml,掌握静态与动态两种生成方式。

文件元数据 API 概述

Next.js 提供了一组特殊文件约定,用于生成搜索引擎和社交平台需要的元数据文件:

文件用途访问路径
favicon.ico / icon.png网站图标/favicon.ico
apple-icon.pngApple 设备图标/apple-icon.png
opengraph-image.pngOpen Graph 分享图片自动生成
robots.txt爬虫规则/robots.txt
sitemap.xml站点地图/sitemap.xml

这些文件可以放在 app 目录的任意层级,Next.js 会自动处理它们。

robots.txt

robots.txt 告诉搜索引擎爬虫哪些页面可以访问、哪些不可以。

静态 robots.txt

最简单的方式是在 app 目录下直接创建 robots.txt 文件:

# app/robots.txt
User-Agent: *
Allow: /
Disallow: /private/
Disallow: /admin/

Sitemap: https://example.com/sitemap.xml

动态 robots.ts

当需要根据条件动态生成爬虫规则时,创建 robots.ts(或 robots.js)文件:

// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/private/', '/admin/'],
    },
    sitemap: 'https://example.com/sitemap.xml',
  }
}

这会生成与上一步静态文件相同的 robots.txt 内容。

针对不同爬虫定制规则

可以为不同的搜索引擎爬虫设置不同的规则:

// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: 'Googlebot',
        allow: '/',
        disallow: '/private/',
      },
      {
        userAgent: ['Applebot', 'Bingbot'],
        disallow: ['/'],
      },
    ],
    sitemap: 'https://example.com/sitemap.xml',
    host: 'https://example.com',
  }
}

输出结果:

User-Agent: Googlebot
Allow: /
Disallow: /private/

User-Agent: Applebot
Disallow: /

User-Agent: Bingbot
Disallow: /

Sitemap: https://example.com/sitemap.xml

robots.ts 是特殊的 Route Handler

robots.ts 默认会被缓存,除非使用了请求时 API 或动态配置选项。

sitemap.xml

sitemap.xml 帮助搜索引擎发现和索引你网站的所有页面。

静态 sitemap.xml

app 目录下直接创建 sitemap.xml

<!-- app/sitemap.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com</loc>
    <lastmod>2026-07-30</lastmod>
    <changefreq>daily</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://example.com/about</loc>
    <lastmod>2026-07-28</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

动态 sitemap.ts

当页面数量多或需要从数据库获取 URL 列表时,使用 sitemap.ts

// app/sitemap.ts
import type { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  // 从数据库获取所有文章
  const posts = await db.query.posts.findMany({
    columns: { slug: true, updatedAt: true },
  })

  const postEntries: MetadataRoute.Sitemap = posts.map((post) => ({
    url: `https://example.com/blog/${post.slug}`,
    lastModified: post.updatedAt,
    changeFrequency: 'weekly',
    priority: 0.7,
  }))

  return [
    {
      url: 'https://example.com',
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1,
    },
    {
      url: 'https://example.com/about',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
    ...postEntries,
  ]
}

拆分大型 sitemap

Google 限制每个 sitemap 文件最多 50,000 个 URL。当页面数量超过限制时,使用 generateSitemaps 函数拆分:

// app/product/sitemap.ts
import type { MetadataRoute } from 'next'

// 告诉 Next.js 需要生成多少个 sitemap 文件
export async function generateSitemaps() {
  const totalProducts = await getProductCount()
  const sitemapCount = Math.ceil(totalProducts / 50000)
  return Array.from({ length: sitemapCount }, (_, i) => ({ id: i }))
}

// 每个 sitemap 文件的内容
export default async function sitemap({
  id,
}: {
  id: Promise<string>
}): Promise<MetadataRoute.Sitemap> {
  const sitemapId = await id
  const start = Number(sitemapId) * 50000
  const end = start + 50000

  const products = await getProducts(start, end)

  return products.map((product) => ({
    url: `https://example.com/product/${product.id}`,
    lastModified: product.updatedAt,
  }))
}

拆分后的 sitemap 可通过 /product/sitemap/0.xml/product/sitemap/1.xml 等路径访问。

其他文件元数据

Favicon 和图标

app 目录下放置以下文件,Next.js 会自动生成对应的 <link> 标签:

  • favicon.ico - 传统 favicon
  • icon.png / icon.jpg / icon.svg - 通用图标
  • apple-icon.png - Apple 设备图标

Open Graph 图片

在路由目录下放置 opengraph-image.png(或 .jpg.tsx),Next.js 会自动将其作为该路由的 OG 图片:

app/
  opengraph-image.png      # 根路由的 OG 图片
  blog/
    opengraph-image.png    # /blog 路由的 OG 图片

更具体的图片会覆盖更通用的图片。例如 /blog 路由会使用 blog/opengraph-image.png 而不是根目录的。

动态 OG 图片

使用 opengraph-image.tsx 可以动态生成 OG 图片(详见第 19 章)。

类型参考

MetadataRoute 命名空间提供了完整的类型定义:

// Robots 类型
type MetadataRoute.Robots = {
  rules:
    | {
        userAgent?: string | string[]
        allow?: string | string[]
        disallow?: string | string[]
        crawlDelay?: number
      }
    | Array<...>
  sitemap?: string | string[]
  host?: string
}

// Sitemap 类型
type MetadataRoute.Sitemap = Array<{
  url: string
  lastModified?: string | Date
  changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'
  priority?: number
}>

小结

  1. robots.txt:静态文件直接创建,动态规则使用 robots.ts
  2. sitemap.xml:静态文件直接创建,动态内容使用 sitemap.ts
  3. 大型 sitemap:使用 generateSitemaps 拆分,每个文件最多 50,000 URL
  4. 文件位置:放在 app 目录的任意层级,更具体的路径优先级更高
  5. 类型安全:使用 MetadataRoute 命名空间获得完整类型提示