bokumin.org

Github

ブログのサイトマップ生成ロジックを変更してGoogle/Bingに再提出した話

A story about changing the sitemap generation logic of a blog and resubmitting it to Google/Bing

 

はじめに

 

最近、この個人ブログに英語版のページを追加しました。
これまでは日本語記事のみで運用してきましたが、ちょくちょく海外からのアクセスもみられたため、多言語対応(i18n)の実装を行いました。

 

しかし、ブログシステム(SSG)のコードを書き換えて記事を翻訳するだけでは片手落ちです。検索エンジンに対して「新しい言語のページが生成された」「URL構造が変更された」という事実を正しく伝播させなければ、せっかく作ったページもインデックスされません。

 

そこで今回は、多言語対応に伴うサイトマップ(sitemap.xml)の再設計と、エンジニア視点で行ったSEO周りのインフラ設定(Cloudflare・WebSubなど)について、その思考プロセスと作業内容を共有します。

 

結論から言うと、今回の構成変更を行うことで、早ければ記事公開から24時間以内(翌日)には検索結果に反映されるレベルの反応速度を実現できました。
ただし、これには「ドメインの信頼性」という前提条件も絡んできますが、そのあたりの関係も含めて解説します。

 


 

1. サイトマップの物理分割と再登録

 

今回の多言語化に伴い、URL構造が変化しました(日本語はルート直下、英語は /en/ 配下など)。
これに合わせて、サイトマップの生成ロジックも変更し、物理ファイルを言語ごとに分割する設計としました。

 

なぜファイルを分けたのか?

 

1つの sitemap.xml に全言語のURLを記述することも仕様上は可能です。しかし、以下の理由から分割を採用しました。
まず一つ目は、管理の問題です。将来的に「英語記事のみ除外したい」「言語ごとにインデックス状況を分析したい」となった際、ファイルが分かれている方がSearch Consoleでの追跡・切り分けが容易になります。
二つ目はパース負荷の軽減をしたかったからです。記事数が増加した際、1つの巨大なXMLファイルをパースさせるより、分割して提示した方がクローラーのリソース消費を抑えられる利点があります。

 

具体的には、以下の2つを生成するようにビルドスクリプトを改修しました。

 

sitemap.xml 日本語記事のみ(/en/を含まないURL)
sitemap-en.xml 英語記事のみ(/en/のみのURL)

 

このようにsitemapのURLが膨大になってきた・管理しきれないなどの時はわけたほうがいいです。

 

Google Search Console (サチコ) への再提出

 

構成が変わったため、Google Search Consoleへの再登録が必要です。古い情報が残っているとクロールエラー(404等)の原因になるため、以下の手順で状態をリセットしました。

 

  1. 管理画面の「サイトマップ」メニューへアクセス。
  2. 既存の(古い)sitemap.xml を選択し、「サイトマップの削除」を実行。
  3. 「新しいサイトマップの追加」フォームから、生成した2つのファイルをそれぞれ送信。
    • sitemap.xml
    • sitemap-en.xml

     

 

Bing Webmaster Tools も忘れずに

 

Googleと同様にBing Webmaster Toolsを開き、サイトマップ機能から上記2つのファイルを送信しました。
BingbotはGooglebotとは異なるサイクルで巡回するため、明示的にエンドポイントを伝えておくことで、インデックスまでの初動レイテンシを短縮できます。

 

【補足】フィード(RSS/Atom)もあわせて登録する

 

見落とされがちですが、Google Search CentralではサイトマップとRSS/Atomフィードの両方を送信することを推奨しています。

 

  • サイトマップ:サイト全体の「地図」。全ページの構造を伝える。
  • フィード (RSS/Atom):最新情報の「通知」。新着コンテンツを素早く伝える。

 

それぞれ役割が異なるため、両方をSearch Consoleに登録しておくことで、クローラーのカバレッジを最大化できます。今回はfeedもサイトマップと同様に追加登録しておきました。

 


 

2. インフラ側の設定

 

検索エンジンに正しい情報を渡すためには、アプリケーション層(サイトマップ生成)だけでなく、インフラ層(配信設定)の最適化も不可欠です。

 

robots.txt の更新

 

robots.txtは、クローラーに対するアクセス制御リスト(ACL)のような役割を果たします。
ここでサイトマップの場所を明示しておかないと、探索型のクローラー以外はサイトマップを発見できない可能性があります。

 

以下のように記述し、2つのサイトマップの場所を正規URL(FQDN)で宣言しました。また、管理画面などのシステム用パスは Disallow に設定し、クロールリソースを浪費させないようにしています。

 

User-agent: *
Disallow: /admin/
Disallow: /drafts/

# Sitemaps
Sitemap: https://example.com/sitemap.xml
Sitemap: https://example.com/sitemap-en.xml

 

CloudflareのPage Rules設定(キャッシュ・バイパス)

 

静的サイトジェネレーター(SSG)でブログを構築し、CDN(Cloudflare等)を経由している場合、「サイトマップのキャッシュ不整合」に注意が必要です。

 

オリジンサーバー上で新しい sitemap.xml が生成されていても、CDNのエッジノードが「古いサイトマップ」をキャッシュし続けている場合、Googlebotに対して古いリスト(Stale Content)を返すことになります。これでは、新規記事がいつまで経っても発見されません。

 

サイトマップは軽量なテキストファイルであり、キャッシュミスによるオリジン負荷は無視できるレベルです。
そこで、Cloudflareの Page Rules 機能を使って、以下の設定を適用しました。

 

  • URLパターン: *domain.com/sitemap*.xml
    • ワイルドカードにより、日本語版・英語版の両方にマッチさせます。

     

  • 設定 (Cache Level): Bypass (キャッシュしない)

 

これにより、クローラーは常にオリジンから生成直後の最新サイトマップを取得できるようになります。

 

作成する際に使用したスクリプトは以下で確認が可能です。

 

 


 

3. 生成ロジックと日付(Lastmod)の制御

 

今回最もこだわったロジック部分です。
XMLサイトマップの <lastmod>(最終更新日)タグについて、言語ごとの運用特性に合わせて出力ロジックを分岐させました。

 

日本語記事(JA)→Modified Date(更新日)を採用

 

日本語記事はオリジナルのコンテンツです。リライトや技術情報の追記を行った場合は、更新事実を検索エンジンに伝えるべきです。
そのため、記事のメタデータにある updatedAt(更新日)を lastmod に出力します。これにより、SERP(検索結果ページ)上で情報の鮮度をアピールできます。

 

英語記事(EN)→Publish Date(投稿日)を採用

 

一方、新設した英語版記事では、あえて publishedAt(投稿日)を出力し、内部的に更新日が変わってもLastmodを変更しない方針としました。

 

理由は以下の通りです。

 

  1. 翻訳チューニングへの対応:
    運用初期は、「言い回しの微調整」や「誤字修正」といったマイナーフィックスが頻発します。
  2. 評価指標の安定化:
    数単語の修正で日付を更新してしまうと、検索アルゴリズムから見て「頻繁に更新されているが、コンテンツの差分が僅少である」という状態になります。これは品質評価においてノイズとなるリスクがあります。

 

あくまでオリジナルは日本語版であり、英語版は翻訳品質の向上による微修正がメインとなるため、「大幅なリライトでない限り、再クロールを要求しない」という安全策を取りました。

 


 

4. クロールバジェットを考慮した通知設計

 

次に、更新通知(Ping)の仕組みです。
「記事公開=即インデックス」を目指す上で重要ですが、エンジニアとしては「クロールバジェット(Googlebotのリソース)」の無駄遣いを避ける設計が求められます。

 

Google Indexing API は不採用

 

「Indexing APIを使えば即時インデックスが可能」という情報も散見されますが、今回は不採用としました。
Googleの公式ドキュメントには、以下のように利用用途が限定されています。

 

Currently, the Indexing API can only be used to crawl pages with either JobPosting or BroadcastEvent embedded in a VideoObject.

 

 

つまり、求人情報やライブ配信のような「高頻度で消滅する、短命なコンテンツ」専用のAPIであり、恒久的なブログ記事での利用は推奨されていません。APIの誤用はガイドライン違反となるリスクがあります。

 

WebSub (旧 PubSubHubbub) と条件付き通知

 

代わりに、標準プロトコルである WebSub を利用します。
しかし、ここでもバッチ処理を考慮した制御が必要です。

 

多言語対応においては、「過去記事を一括翻訳してデプロイする」といった一括処理が発生します。
この際、無条件に通知機能を有効にしていると、数百件単位のPingが瞬時にGoogleへ送信されます。検索エンジン側から見れば、以下のような挙動になります。

 

  1. 大量の更新通知を受信し、クローラーリソースを割り当てる。
  2. サイトマップを確認すると(前述のLastmod設定により)日付は変化していない。
  3. あるいは、コンテンツ差分が極めて小さい。
  4. 「過剰な通知を行うドメイン」と判定され、以降のクロール頻度が低下する。

 

これはクロールバジェットの浪費です。
そのため、サイトマップ生成スクリプトには以下の条件分岐を実装しました。

 

  • 新規記事の単体公開時:WebSub通知を 送信する
  • 全記事再生成などのバッチ処理時:WebSub通知を 送信しない

 


 

5. 実際の効果とドメイン信頼性の相関

 

これらの設定を適用した結果、Search Consoleのログ上では、記事公開から数分後にはクローラーが到達し、翌日(約24時間以内)にはインデックス登録されるケースを確認しました。

 

ただし、技術的な設定だけでこの速度が出るわけではありません。「ドメインの信頼性(Authority)」との掛け合わせが重要です。

 

技術設定とドメイン評価の関係性

 

Googlebotの巡回頻度は、ドメインの運用実績やコンテンツ品質に基づいて割り当てられます。

 

  • 技術設定(今回の記事内容):
    クローラーに対して更新シグナルを的確に送信し、発見(Discovery)までのレイテンシを最小化するための実装
  • ドメインの信頼性:
    Googleが「このドメインに対して優先的にクロールバジェットを割り当てる価値がある」と判断するためのアルゴリズム的な根拠

 

今回の「翌日インデックス」という結果は、ブログがある程度の期間運用されており、過去の記事更新実績から一定の信頼スコアを得ていたため、技術的な施策(WebSub + 最新サイトマップ)がボトルネックにならず機能したと言えます。

 

逆に言えば、どんなに優れたCI/CDパイプラインや通知システムを組んでも、運用初日の新規ドメインでは同様の速度は出ません(サンドボックス期間などが影響するため)。
しかし、運用期間が長くなった時にインデックスが遅い場合は、今回紹介したような技術的負債(キャッシュ設定や通知ロジックの不備)が原因である可能性が高いです。

 


 

まとめ

 

「サイトマップは自動生成して終わり」と思われがちですが、多言語対応やSSG運用といった要件が加わると、エンジニアリングとして考慮すべき点は多岐にわたります。

 

  • 物理ファイルの分割による管理性の向上
  • CDNキャッシュのバイパスによるデータ整合性の確保
  • Lastmodのロジック分岐による評価の安定化
  • WebSubの条件付き送信によるリソース最適化

 

これらの設定は、一度パイプラインに組み込んでしまえば自動的に機能します。
誰かの参考になれば幸いです。

 

おわり