Next.js App Router で XML サイトマップを作成する
SEO 関連の知識をつけておきたく、当ブログサイトで XML サイトマップを作成してみようと思い少し調べて実装してみたので、個人的な備忘録の意味も込めて書き残しておきます。
XML サイトマップとは?
XML サイトマップは、検索エンジンにウェブサイトの重要なページやファイルを伝えるために設置するファイルであり、ウェブサイトの「道標」とも言えるものです。
サイトマップの基本的な構造は以下のようなものです。
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.kazumasahirata.co/</loc>
</url>
<url>
<loc>https://www.kazumasahirata.co/blog</loc>
<lastmod>2024-02-23T00:00:00.000Z</lastmod>
</url>
<url>
<loc>https://www.kazumasahirata.co/blog/kazumasahirata-co</loc>
<lastmod>2024-02-07T00:00:00.000Z</lastmod>
</url>
</urlset>
URL や日付が並んでいることから予想がつくかと思いますが、上記では例えば https://www.kazumasahirata.co/blog
という URL のページの最終更新日時が 2024-02-23T00:00:00.000Z
である、というようなことを表現しています。
サイトマップの設置によって、検索エンジンはサイトを効率的にクローリング(ページの解析とインデックスの作成)することができ、結果的に SEO に寄与することがあります。
XML サイトマップは必要か?
では XML サイトマップは常に設置すべきなのかというと、そうでもありません。Google for Developersによれば、サイト内で各ページが適切にリンクされている限り、Google のクローラーはリンクを辿ってほとんど全てのページをクロールできます。
クローラーが全てのページを検出できない状況
ただし、以下のような特定の条件下ではクローラーがすべてのページを検出できないことがあります。
- 大規模ウェブサイト
- ページ数が数千・数万に登るような大規模サイトでは、ページ同士のリンクが不十分であったり、ページ階層が深すぎて、クローラーがページに到達できない場合があります。
- また、頻繁にページが追加・更新されることで、クローラーが最新の内容を把握するのが困難な場合があります。
- リッチメディアコンテンツ(画像や動画など)や JavaScript を多用するウェブサイト
- 画像や動画が豊富なサイトはテキストコンテンツが乏しいことがあり、クローラーがページ内容を理解しづらい場合があります。
- クローラーの動的コンテンツレンダリングは完全ではないため、JavaScript を多用するサイトの内容を正しく把握できない場合があります。
- 新規ウェブサイト
- 新しく公開されたサイトは、他のサイトからのリンクが少ないために、クローラーがページを発見しづらい場合があります。
こういった状況では、XML サイトマップでリンク構造や更新の情報を伝えることで、クローラーが効率的にインデックスを作成できるよう促すことができます。その結果として SEO の観点でもメリットが得られるということです。
XML サイトマップが不要なサイト
すなわち、ページ数が少なく、リンク構造がシンプルで、画像等のリッチメディアや複雑な JS が少ない小規模なサイトでは、クローラーは問題なくインデックスを作成できる可能性が高く、XML サイトマップは必ずしも必要ではないということです。
私のブログサイトも現状非常にシンプルでページ数も限られているためサイトマップは不要と思っていますが、設置することによる SEO 上のペナルティはないので念のためということと、SEO への理解を深めるためにも設置してみています。
XML サイトマップの項目
冒頭でも例を示しましたが、改めて XML サイトマップの構造を確認しておきます。
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.kazumasahirata.co/</loc>
<lastmod>2024-02-01T00:00:00.000Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
...
</urlset>
urlset
がルート要素になっており、その中の url
タグが個々のページ情報を表しています。そして url
タグはそれぞれ以下の 4 つの属性を持ちます。
属性 | 必須 | 説明 |
---|---|---|
loc | Yes | ページの URL |
lastmod | No | ページの最終更新日時 |
changefreq | No | ページの更新頻度 |
priority | No | サイト内でのページの優先度 |
表のとおり必須の属性は loc
のみでその他はオプショナルです。ウェブサイトの規模が大きい場合や特定のページを優先的にクロールして欲しい場合は、changefreq
や priority
を指定することで、検索エンジンのインデックス作成効率を向上できます。ちなみに、当サイトでは公開したブログ記事を更新する場合もあるので lastmod
を活用しています。
XML サイトマップの正式な仕様は sitemaps.org で定義できますので、詳細はそちらを参照してください。
Next.js App Router で XML サイトマップを作成する
XML サイトマップの役割と構造の理解を深めたところで、実際に Next.js App Router でサイトマップを生成する方法を確認します。
Next.js では XML サイトマップを作成するための独自の方法が提供されています。それが app/
ディレクトリ直下に sitemap.xml
、sitemap.js
、sitemap.ts
のいずれかのファイルを作成するという方法です。
静的なサイトマップを配信したければ sitemap.xml
を作成すればよいですし、動的に生成したければ sitemap.ts
で生成処理を書くこともできます。ブログのように内容が頻繁に追加・更新されるサイトであれば、sitemap.ts
で処理を定義することでサイトマップを自動で更新させたいところですので、今回はこちらの方法を紹介したいと思います。
実装はとてもシンプルで、sitemap.ts
でサイトマップの内容を定義するデフォルトエクスポート関数を作成するだけです。以下のサンプルコードのように、サイトマップの属性を JSON 形式で返します。TypeScript を使用する場合はエディタの型補完も効くので、安全に効率的に記述できます。
import { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: "https://kazumasahirata.co/",
lastModified: new Date("2024-02-01T00:00:00.000Z"),
changeFrequency: "weekly",
priority: 0.8,
}
];
}
さらに、ブログ記事のリストに従って動的にサイトマップを生成するコードも以下のように書くことができます。
import { MetadataRoute } from "next";
import { WEB_URL } from "@/config";
export default function sitemap(): MetadataRoute.Sitemap {
const posts = await getBlogPosts();
return [
{
url: WEB_URL,
},
...posts.map((post) => ({
url: `${WEB_URL}/blog/${post.id}`,
lastModified: post.updatedAt ?? post.createdAt,
})),
];
}
API を利用してブログ記事一覧を取得し、記事ごとに URL と最終更新日 lastModified
を計算しています。こうすることで記事の新規追加や更新の際にも XML サイトマップは手動修正なく最新の状態に保たれるので、管理が楽ですね。
Next.js 公式ドキュメントではこの他にも、サイトマップをより細かく管理するための機能などが紹介されていますので、是非参照してください。
まとめ
今回は私自身の SEO への理解と備忘録も兼ねて、XML サイトマップについて知識をおさらいし、簡単ながら Next.js App Router で動的に XML サイトマップを生成する方法を紹介しました。
Next.js App Router では sitemap.xml の他にも SEO 関連の設定やサイトメタデータを動的に管理できる仕組みがいくつか用意されています(robots.txt、OGP など)ので、今後もキャッチアップを続けていきたいと思います。
ご覧いただきありがとうございました。