Supabase+Next.jsで動的にsitemap.xmlを生成する
概要
オンデマンドISRで作られたブログのインデックスが全くされないのでサイトマップ載せ異性を追加してみる。
環境
参考
- Route Handlers: Non-UI Responses | Next.js
- Metadata Files: sitemap.xml | Next.js
- Next.js 15 でサイトマップを生成するガイド(Zenn)
実装手順
- Supabase クライアントを設定
app/sitemap.xml/route.ts
を作成robots.txt
にサイトマップを追加
実装
Supabase のクライアントを登録しておく
これは後々 Vercel側にも登録しておいてね。
.env.local
に追加。
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-service-role-key
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
src/app/sitemap.xml/route.ts
を作成
src/app/sitemap.xml/routes.ts
日本語が含まれるURLの場合は encode が必要。
import { supabase } from "@/libs/supabase";
export async function GET() {
const { data: posts, error } = await supabase
.from("articles")
.select("category, slug, updated_at")
.eq("private", false);
if (error || !posts) {
console.error("Failed to fetch posts for sitemap:", error);
return new Response("Failed to generate sitemap", { status: 500 });
}
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL!;
const urls = posts
.map((post) => {
// 日本語が含まれる場合はencodeが必要.
const encodedSlug = encodeURIComponent(post.slug);
const location = `${baseUrl}/${post.category}/${encodedSlug}`;
const lastModified = new Date(post.updated_at || new Date()).toISOString();
return `<url><loc>${location}</loc><lastmod>${lastModified}</lastmod></url>`;
})
.join("\n");
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>`;
return new Response(xml, {
headers: {
"Content-Type": "application/xml",
},
});
}
Supabaseのクライアントは次のようにしている。
import { createClient } from "@supabase/supabase-js";
// 環境変数から取得
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_KEY = process.env.SUPABASE_KEY;
export const SUPABASE_IMAGE_BUCKET = process.env.SUPABASE_IMAGE_BUCKET;
if (!SUPABASE_URL || !SUPABASE_KEY || !SUPABASE_IMAGE_BUCKET) {
throw new Error("Missing Supabase credentials. Check your environment variables.");
}
export const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
robots.txt
にサイトマップを追加
User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap.xml
本番環境にデプロイ後
本番環境にデプロイしたらhttps://yourdomain.com/sitemap.xml
を確認する。
https://yourdomain.com/sitemap.xml
にアクセスすれば、リアルタイムで sitemap を取得できる- Google Search Console にサイトマップのURL を登録する。
- これで記事追加のたびに自動でクロール対象になるはず。
デプロイ後すぐにやっても「取得できませんでした」になっちゃった。
数日してもダメみたい!!!!一度サイトマップの削除をする。
そいで登録し直したけど変わらず。
かなり時間がかかるよう。
Search ConsoleでのXMLサイトマップ「取得できませんでした・読み込めませんでした」エラーの正体 の記事より
(著者訳)新しいサーチコンソールでの「読み込めませんでした」の表示はサーチコンソール側のバグ。実際の裏側ステータスでは「ペンディング」(保留中)になっていますよ! Sitemap could not be read in new GSC - Google Search Central Community ということで、Search Consoleにおける表示バグのようです。
読み込みませんでしたは「保留中」の意味でいいらしい。
こちらのXMLサイトマップ「取得できませんでした・読み込めませんでした」エラーの発生から3〜4ヶ月程度を経過した頃に、ふと思い出して確認しに行ったところ、正常に「成功」のステータスとなっていました。
3〜4ヶ月!!!!長い!!!
ということでとりあえずこのまま放置!登録されたら追記する。
おわりに
Next.jsのブログ記事がインデックスされない理由を調査した結果、とりあえずサイトマップを作ることにしたわけだが、効果の方は後ほど。
インデックスしてくれ〜〜〜〜〜〜〜〜!
おまけ: Firefox のエラーは関係なし
生成された XMLが悪いのかと思って調査していた時に遭遇したエラーについて。
sitemap.xml
を Firefox の Developer Tools で見るとエラーが出ている。
XML パースエラー: 整形式になっていません。
URL:
行番号: 7, 列番号: 61:
cmd+U
で該当箇所を見てエンコードを変えてみてもダメだったので超簡略化してみてみることに。
export async function GET() {
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/test</loc><lastmod>2025-01-01T00:00:00Z</lastmod></url>
</urlset>`;
return new Response(xml, {
headers: {
"Content-Type": "application/xml; charset=utf-8",
},
});
}
で、これでも同じエラーが出ている。Firefox のエラー???
他の人のサイトを見てきても同様のエラーが出てるのでこれは無視してokそう。
