Next.jsのSSGのサイトでXMLサイトマップ(sitemap.xml)を自動生成する機会がありました。自動生成には手軽に利用できる next-sitemap を利用しました。生成自体は手軽にできますが、ところどころ詰まってしまったので、これから利用する方に向けてまとめていこうと思います。
環境
next:11.0.2-canary.22
next-sitemap:2.5.17
node:14.18.3
※ node 14.18.3 以上でないと next-sitemap が動作しないので注意。
サイトマップの作成手順
設定ファイルの作成
プロジェクト直下にnext-sitemap.js
を作成します。
ファイル名は変更可能なので、next-sitemap.config.js
などにしても大丈夫です。
/** @type {import('next-sitemap').IConfig} */ // このコードがあるとコード補完が可能になる
module.exports = {
siteUrl: process.env.SITE_URL, // envファイルにデフォルトでアクセス可能
outDir: './out', // SSGだとビルド後にoutディレクトリができるので合わせる
generateRobotsTxt: true, // (optional). // robots.txtが必要な場合は指定する
// ...other options
};
ビルド時の設定
ビルド後にサイトマップが生成されるようにコマンドを追加します。
{
"build": "next build",
"postbuild": "next-sitemap"
}
設定ファイルの名前を変更している場合は、オプションを指定します。
{
"build": "next build",
"postbuild": "next-sitemap --config <custom-config-file>.js"
}
サイトマップの作成
これで作成の準備は整ったので、ビルドを実行するとサイトマップが作成されます。
npm run build
./out
ディレクトリにsitemap.xml
sitemap-0.xml
が作成されているはずです。
なぜファイルが2つ作成されているかというと、next-sitemap v2.x以降、インデックスサイトマップがデフォルトとなっているためです。サイトマップは50MBを超える場合、ファイルを分割する必要がありますが、その機能を提供してくれています。
インデックスサイトマップについて詳しくはこちら。
サイトマップの最適化
サイトマップの作成まで完了しましたが、サイトマップとして不要な情報があるので最適な形に直していきます。
設定ファイルの作成と同じ設定で作成した場合、サイトマップはこのようになっていると思います。
// sitemap-0.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://example.com</loc><changefreq>daily</changefreq><priority>0.7</priority><lastmod>2022-04-17T10:00:00.000Z</lastmod></url>
<url><loc>https://example.com/xxx</loc><changefreq>daily</changefreq><priority>0.7</priority><lastmod>2022-04-17T10:00:00.000Z</lastmod></url>
<url><loc>https://example.com/xxx/yyy</loc><changefreq>daily</changefreq><priority>0.7</priority><lastmod>2022-04-17T10:00:00.000Z</lastmod></url>
...
</urlset>
この中で直す項目は、下記の4点です。
- loc:日本語などがそのまま書き出されてしまうのでURLエンコードする
- changefreq:不要なので削除
- priority:不要なので削除
- lastmod:全て現在日付だと問題があるので、ページの最終更新日にする
理由としては、Googleがこのように記載しているためです。
・サイトマップ ファイルは UTF-8 エンコードで作成し、適切に URL をエスケープする必要があります。
・Google は、<priority> と <changefreq> の値を無視します。
・Google は、<lastmod> 値が一貫して正確であることを(ページの最終更新との比較などにより)検証できる場合に、この値を使用します。
参考:https://developers.google.com/search/docs/advanced/sitemaps/build-sitemap?hl=ja
書き換え
サイトマップの書き換えには、設定ファイルでオプションのtransform
を使用します。transform
はサイトマップの相対パス毎に実行される関数で、戻り値の内容に情報を書き換えることができます。
module.exports = {
siteUrl: process.env.SITE_URL || 'https://example.com',
outDir: './out',
generateRobotsTxt: true, // (optional)
transform: async (config, path) => {
if (isIgnorePath(path)) {
return null; // nullを返すとサイトマップから除外できる
}
return {
loc: encodeURI(path), // エンコードしたURLを返す
lastmod: customLastmod(path), // パスを元に、ページ毎の最終更新日を返す
};
},
};
const isIgnorePath = (path) => {
return {無視するか}
};
const customLastmod = (path) => {
return {最終更新日}
};
こちらで作成するとこのようになります。
// sitemap-0.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://example.com</loc><lastmod>2022-04-15</lastmod></url>
<url><loc>https://example.com/xxx</loc><lastmod>2022-04-12</lastmod></url>
<url><loc>https://example.com/xxx/yyy</loc><lastmod>2022-04-11</lastmod></url>
...
</urlset>
lastmodの時刻については、標準のサイトマップ プロトコルに省略可能と記載されているので省略して問題ありません。
APIの値を利用する場合の注意点
私たちのプロジェクトでは、各ページ情報をAPIでも提供しているので、APIから更新日時を取得することが可能でした。
サイトマップの生成にAPIを利用する際には、1つ気をつけた方が良い点あります。
それは、transformはパス毎に関数が実行されてしまうので、この中だと複数回通信が走り処理が重くなってしまいます。
なので、一度だけ取得して、その値を使い回すようにしましょう。
module.exports = {
...
transform: async (config, path) => ({
loc: encodeURI(path),
lastmod: customLastmod(path),
}),
};
const postsPromise = getPosts();
const customLastmod = async (path) => {
const { data: posts } = await postsPromise;
const post = posts.find(...);
return post.updatedAt;
};
これで最適化されたサイトマップが作成できました!