Next.jsでサイトマップを生成する ― 静的・動的・大規模分割
投稿日:2026.06.15
CATEGORY
- Web開発
サードパーティパッケージが不要になった
Pages Router時代、Next.jsでサイトマップを生成するにはnext-sitemapのようなサードパーティパッケージを使うのが一般的でした。App Routerにはサイトマップ生成のファイル規約が組み込まれており、外部パッケージなしで対応できます。
この記事では、App Routerのサイトマップ生成機能を規模ごとに整理します。内容はNext.js公式ドキュメントに基づいています。
3つのアプローチを規模で選ぶ
Next.jsのサイトマップ実装には主に3つのアプローチがあります。
| アプローチ | 向いているケース |
|---|---|
| 静的ファイル直置き | 小規模・変更頻度が低い |
| 動的生成(sitemap.ts) | 中規模・DBやCMSと連携 |
| 分割生成(generateSitemaps) | 大規模・50,000URL超 |
1. 静的ファイルの直置き
最もシンプルな方法は、app/sitemap.xmlに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>2024-01-01</lastmod>
</url>
<url>
<loc>https://example.com/about</loc>
</url>
</urlset>
このファイルは/sitemap.xmlとして配信されます。数ページ程度の静的サイトや、サイトマップをビルド時に別ツールで生成している場合に適しています。
2. 動的生成(sitemap.ts)
Next.js 13.3.0以降、app/sitemap.tsにdefault export関数を書くことで動的にサイトマップを生成できます。この関数はMetadataRoute.Sitemap型の配列を返します。
import type { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
// DBやAPIからURLの一覧を取得
const posts = await fetchAllPosts()
const postUrls = posts.map((post) => ({
url: `https://example.com/blog/${post.slug}`,
lastModified: post.updatedAt,
}))
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 1,
},
...postUrls,
]
}
型の各フィールドは次のとおりです。
{
url: string // 必須
lastModified?: Date | string // 省略可
changeFrequency?: // 省略可
| 'always' | 'hourly' | 'daily'
| 'weekly' | 'monthly' | 'yearly' | 'never'
priority?: number // 省略可 (0.0 〜 1.0)
alternates?: { // hreflang対応 (Next.js 14.2.0+)
languages?: Record<string, string>
}
images?: string[] // 省略可
videos?: ...[] // 省略可
}
changeFrequencyとpriorityについて: これらのフィールドはサイトマップの仕様にある項目ですが、Googleはこれらの値を基本的に無視します。順位への影響はありません。クロール頻度の調整に使えると期待されることがありますが、公式に効果が保証されているわけではありません。設定しても問題はありませんが、実装コストをかける優先度は低いと考えてよいです。
sitemap.tsはRoute Handler扱いで、既定ではキャッシュされます。動的なデータを毎回取得したい場合はキャッシュ設定を明示的に変更します。
3. 大規模分割(generateSitemaps)
Googleのサイトマップ仕様では、1ファイルあたりの上限は50,000 URLです。それを超える場合はgenerateSitemaps()を使って分割します。
import type { MetadataRoute } from 'next'
export async function generateSitemaps() {
// 分割数を決める。例: 総URLを50,000で割った数
return [
{ id: 0 },
{ id: 1 },
{ id: 2 },
]
}
export default async function sitemap({
id,
}: {
id: number
}): Promise<MetadataRoute.Sitemap> {
// id に対応するURLの範囲を取得
const posts = await fetchPostsBatch(id)
return posts.map((post) => ({
url: `https://example.com/blog/${post.slug}`,
lastModified: post.updatedAt,
}))
}
この設定で/sitemap/0.xml、/sitemap/1.xmlのように複数のサイトマップファイルが生成されます。
Next.js 16.0.0以降の変更: idパラメータの型がPromise<string>になりました。16.0.0以降ではawaitが必要です。使用しているバージョンを確認してください。
// Next.js 16.0.0以降
export default async function sitemap({
id,
}: {
id: Promise<string>
}): Promise<MetadataRoute.Sitemap> {
const resolvedId = await id
// ...
}
robots.tsとの連携
サイトマップのURLをrobots.txtに記載することで、クローラーがサイトマップを発見しやすくなります。app/robots.tsのsitemapフィールドに記載すると自動で出力されます。
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
},
sitemap: 'https://example.com/sitemap.xml',
}
}
分割サイトマップを使っている場合は、サイトマップインデックスファイルのURLを指定するか、複数のサイトマップURLを配列で指定します。
Pages Routerでの対応
App Routerのファイル規約(app/sitemap.ts、app/robots.ts)はApp Router専用です。Pages Routerを使っているプロジェクトでは使えません。Pages RouterではAPI Routeを使った実装か、next-sitemapのようなパッケージが必要です。
ここまでをまとめると
Next.js App Routerのサイトマップ機能は、規模に応じて3つの方法から選びます。
- 静的直置き:
app/sitemap.xmlを配置するだけ。小規模向け - 動的生成:
app/sitemap.tsでDBやAPIと連携。中規模向け - 分割生成:
generateSitemaps()で50,000URL超に対応。大規模向け
changeFrequencyとpriorityはGoogleが基本的に無視するため、実装の優先度は低いです。next-sitemapはApp Routerでは不要で、組み込みの機能で同等以上のことができます。
robots.tsでサイトマップのURLを記載することで、クローラーへの案内を一箇所にまとめられます。
出典
- Next.js サイトマップファイル規約: https://nextjs.org/docs/app/api-reference/file-conventions/metadata/sitemap
- Next.js generateSitemaps: https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps
- Next.js robots.ts: https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots

