動的sitemap・robotsの生成とキャッシュ

商品の増減に自動で追従するsitemap.xmlの生成と、生成コストを抑えるキャッシュ・監視の設計

sitemaprobots.txt動的生成キャッシュインデックス
読了時間: 13分

はじめに

sitemap.xmlは、検索エンジンに「このサイトにはこんなページがあります」と一覧を渡すための地図のようなファイルです。robots.txtは、サイトのどこを見に来てよいかを伝えるメモ。どちらも普段まったく意識しない部分ですが、ヘッドレス構成にすると自分で用意することになります。

ShopifyのようなECプラットフォームは標準でsitemapを自動生成してくれますが、それはプラットフォーム側のURLの一覧であって、自作したフロントエンドのURLは1つも載っていません。robots.txtも同じで、自作フロントのドメインで配信されるものは自前で返す必要があります。

商品が数千から数万SKUあり、しかも日々入れ替わるECで、この2つを手作業で維持するのは現実的ではないですよね。この記事では、モーターサイクル用品を扱うヘッドレスECで、sitemapとrobotsをどう自動生成し、生成コストをどう抑え、どこで事故を防いでいるのかを解説します。

そのサイトのURLだけを自動で集める

販売チャネルを基準に集める

sitemapに載せるべきURLは、そのサイトに実際に存在するページだけです。当たり前のようですが、1つのバックエンドで複数のECサイトを運営していると、ここの作り方で差が出ます。

商品の出し分けには販売チャネル(どのサイトにその商品を並べるかを管理画面のチェックボックスで決める機能)を使っているので、sitemapの生成もこれを基準にしました。管理用のAPIに「このチャネルに公開されている商品・カテゴリ」を問い合わせ、返ってきたハンドル(URLに使われる商品固有の文字列)と更新日時からURLを組み立てます。

この作りにしておくと、商品の公開先を管理画面で切り替えるだけでsitemapの中身も変わります。コードの修正もデプロイも不要で、店舗の担当者だけで完結する。運用面でいちばん負担が減ったのが、この部分でした。

固定ページは一覧として持つ

会社案内やFAQ、規約といった固定ページは商品データから導けないので、パスと更新頻度、優先度をコード側に一覧として持たせています。ページを増やしたときに追記が必要になりますが、数が限られるので手作業でも回ります。

動的sitemap生成の流れ
リクエスト受信

検索エンジンが sitemap.xml を取りに来る

キャッシュ確認

組み上がったsitemapが残っていればそれを返す

材料の取得

無ければ販売チャネル基準で商品・カテゴリを取り直す

組み立てと保存

URL一覧を組み立てて返し、結果をキャッシュに保存

この4段階は、「作り置きがあれば使い、無ければ取り直して作る」という一連の判断です。

生成コストをキャッシュ2段で抑える

毎回フル生成すると問い合わせが積み上がる

数万件ぶんのデータを外部から取得してXMLに組み立てる処理は、当然ながら軽くありません。クローラー(検索エンジンがページを読みに来るプログラム)は思っている以上の頻度でsitemapを取りに来るので、毎回フル生成していると外部への問い合わせ回数もサーバー負荷も無視できない量になります。

配信側と材料の保管庫で期限を分ける

そこでキャッシュを2段構えにしました。配信側では組み上がったsitemapを1日、材料の保管庫では取得した商品・カテゴリのデータを数時間から半日、それぞれ保持します。

保管庫の保存期間は種類ごとに変えました。商品とブログ記事は6時間、カテゴリは12時間といった具合で、更新頻度の高いものほど短くしておく、という単純な考え方。配信側には「期限が切れたら、まず古い内容を返しつつ裏で作り直す」という指定を入れてあり、期限切れ直後に来たクローラーが生成完了まで待たされることがありません。

キャッシュの有無による違い
BEFORE
キャッシュなし

毎リクエストで数万件を取得・生成。外部への問い合わせが積み上がる

AFTER
2段のキャッシュあり

有効期限内は作り置きを再利用。実際に取りに行く回数が大きく減る

同じ内容を何度も作り直さずに使い回している、というだけの話です。

失敗しても正常なsitemapを返す

エラーが続くとクロール頻度そのものが落ちる

sitemapの生成は、外部のAPIやキャッシュの保管庫といった複数の要素に依存しています。どれか1つが応答しなければ、当然エラーになる。最初にやってしまったのが、エラー時にそのままサーバー内部のエラー(500)を返す作りにしていたことでした。

sitemapが繰り返しエラーを返すと、検索側では取得失敗として記録され、そのサイトへの再クロールの頻度自体が落ちます。新商品の反映が遅れるだけでなく、すでに知られているURLの巡回間隔まで延びる。数時間の障害の影響が、その後しばらく残ることになります。

最小限のsitemapに短い期限をつける

今は、どんなエラーが起きても必ず正常(200)を返すようにしています。商品データが取得できなかった場合は、固定ページだけを並べた最小限のsitemapを組み立てて返す。そのときだけキャッシュの期限を10分と短くしておき、復旧したら早めに完全版へ戻る仕組みです。

掲載URLが少なくなっても、正常なsitemapとして届けることを優先する。目立たない判断ですが、クロール頻度の維持に直結しました。

「クロールさせない」と「検索結果に出さない」は別の指定

クロールを止めると、出さない指示も読まれない

robots.txtも動的に生成しています。内容は素直なもので、全体のクロールを許可したうえで、管理画面・API・マイページの3つを対象外にし、最後にsitemapの場所を書いておく形です。

ここで一度、逆の対応をしてしまいました。検索結果に出したくないサイトに対して、robots.txtで一律にクロールを禁止したんです。クロールを禁止すると検索エンジンはそのページを取得できなくなり、ページ内に書いてある「検索結果に出さないでください」という指示も読めません。結果として、外部からリンクされているURLだけがインデックスに残り、検索結果には中身の分からない状態で表示され続けました。

許可したうえで、ページ側で外す

正しい対処は逆でした。クロールは許可したうえで、ページ側で検索結果に出さない指示(noindex)を返す。クローラーにページを取得させ、そこに書かれた指示を読ませることで、はじめて検索結果から外れます。

表の3行目が、実際に使うことの多い組み合わせです。クロールを許可しておくと広告用の商品フィードの検証も通るので、検索結果には出さないが機械には見せる、という状態を作れます。

URLの数と表記を揃えて監視する

URL数を見て異常に早く気づく

自動生成の弱点は、壊れたときに誰も気づかないことです。sitemapの中身が半分に減っていても、画面には何の変化もありません。検索順位が下がってから調べ始める、という順番になりがちで、そのころには数週間経っています。

対策として、キャッシュに保存されている商品数・カテゴリ数・記事数と、合計URL数、最後に生成した日時を返す管理用のAPIを用意しました。前回より大きく減っている時点で、データ取得の失敗や設定ミスを疑えます。あわせて、キャッシュを手動で消すための口も同じAPIに付けました。商品を大量に入れ替えた直後など、期限切れを待たずに作り直したい場面があるためです。

URL表記を1段の301でまとめる

sitemapに載せるURL自体の表記も揃えておく必要があります。同じ商品ページなのに大文字混じりのURLや、色の選択状態を示すパラメータが付いたURLが混在していると、検索エンジンからは別ページとして扱われ、本来1ページに集まるべき評価が分散してしまう。

ここはサイトの入口で動く軽い処理(Edgeミドルウェア)を使い、小文字への統一・不要なパラメータの除去・末尾スラッシュの調整を1段の301リダイレクトでまとめて行っています。301は「恒久的に移動した」という意味なので、評価も正規のURLへ引き継がれます。

入口でまとめて処理するので、アプリ本体に届く前に軽く正規化でき、全ページに同じルールを効かせられます。

まとめ

sitemapとrobotsの自動生成で押さえるのは4点です。販売チャネルを基準にそのサイトのURLだけを集めること、キャッシュを2段構えにして生成コストを抑えること、エラー時も必ず正常な最小限のsitemapを返すこと、そしてURL数を監視できる仕組みを持つこと。

加えて、検索結果に出したくないページはrobots.txtで塞ぐのではなく、クロールを許可したうえでページ側の指示で外す。知らないと逆の対応をしてしまうところです。

ここまでの「SSRでの情報出力」「構造化データ」「動的sitemapとURL正規化」が揃うと、ヘッドレス化で止まったSEOの土台はひととおり戻ります。