全国5,095校のサイトマップを、今日1日で作り直した話
住所を入れると通える高校が地図に出てくる、Manabi Mapという個人開発のサービスをやっています。
今日、47都道府県・5,095校のデータがようやく全部揃いました。区切りがいいので、検索まわりの掃除を一気にやることにしたら、朝から晩まで潰れました。
きっかけはGoogle Search Consoleの数字でした。「ページにリダイレクトがあります」の欄に、5,095という見覚えのある数字が並んでいました。今日投入した学校数と、桁までぴったり一致していました。

「リダイレクトがあります」の5,095件、全部同じ理由だった
学校ページを1本、curlで叩いてみました。
$ curl -sI https://manabi-map.app/school/xxxxxxxx
HTTP/1.1 308
location: https://manabi-map.app/school/xxxxxxxx/末尾スラッシュなしでアクセスすると、スラッシュ付きに308でリダイレクトされます。ここまでは普通です。ファイルの置き場所が `/school/xxxxxxxx/index.html` なので、ホスティング側がディレクトリとして扱ってスラッシュを補うのは仕様どおりでした。
問題は、リダイレクトされた先のページでした。開いてcanonicalタグを見たら、スラッシュ無しの、さっき308された元のURLを指していました。
自分自身に向かって「こっちに行け」と言っておきながら、着いた先で「本物はさっきの場所だよ」と言い返している状態です。5,095校ぶん、全部が自分の尻尾を追いかけていました。
直す理由は「順位」じゃなかった
正直に書くと、この308自体はGoogleの検索順位にはほぼ効きません。クロール予算がどうこう言われるのは1万ページを超えて日次更新するようなサイトの話で、5千ページ程度のうちのサービスはそもそも対象外です。
それでも直した理由は1つでした。GSCの「リダイレクトがあります」というカテゴリに5,095件吸われてしまうと、その下にある本当に見たいカテゴリ、「検出 - インデックス未登録」の推移が読めなくなるからです。掃除しないと、次に何が起きているかを観測できません。
ついでに見つけた、もう2つの放置案件
直しついでにサイトマップ全体を見返したら、他にも気になる箇所が出てきました。
・サイトマップに検索結果ページ(/search)が載っていたこと。この実体はSPAのフォールバックで、開いてみるとcanonicalはトップページを指していました。サイトマップには載せているのに「これはトップと同じです」と申告している、矛盾した状態です
・更新日時(lastmod)が、5千件を超える全URLでビルドした日の日付に一律で埋まっていたこと。毎回リリースするたびに、全校が「今日更新されました」と主張することになります。こういうlastmodをGoogleが信用しなくなることは、Search Centralのドキュメントにわりとはっきり書いてあります
直したのは実質3行、確認に半日
サイトマップを生成しているスクリプトを開いて、実際に手を入れたのは3箇所でした。
・学校ページのURLとサイトマップの両方に、末尾スラッシュを付けて揃える
・サイトマップから/searchを外す
・lastmodをビルド日ではなく、各校のデータの更新日から出す
更新日のフィールドは、学校データのJSONにちゃんと入っていました。実際に確認すると11種類の日付にばらけていて、一律の日付になっていたのはスクリプト側だけの問題だったと分かりました。
直すこと自体はすぐ終わったのですが、確認には時間がかかりました。ビルドしてローカルの出力を覗いて、プレビュー環境に上げて、curlでcanonicalとサイトマップを何度も叩いて。ここで1つ拾ったのですが、デプロイした直後の数秒から十数秒は、古い内容のままエッジに残っていることがあります。1回目の確認で「直ってない」と焦ったのですが、10秒ほど待ってもう一度叩いたら正しい内容に変わっていました。デプロイ直後は、ブランチのエイリアスURLよりデプロイ固有のURLで先に確認したほうが早い、というのも今日拾った小さな知見です。
1.68MBのファイルが、4時間おきに無駄取りされていた
キャッシュの設定も見直しました。学校データ全件を詰めた1.68MBのJSONファイルには、本来「1年キャッシュしていい」という設定を入れていたはずでした。
ところが実際に本番のヘッダーを見ると、その設定が付いていませんでした。設定ファイルのパターンを見直すと、拡張子の指定がずれていて、実際に配信されているファイル名とは一度もマッチしていませんでした。
しかも別のルールと組み合わさって、マニフェストファイルのほうには「キャッシュするな」と「1年キャッシュしろ」が同時に乗る、矛盾したヘッダーが返っていました。パターンを1行直したら両方いっぺんに解決しました。地味な修正ですが、これのせいでユーザーの端末が4時間おきに1.68MBを律儀に再取得していたと思うと、けっこう長いこと無駄なことをさせていたことになります。
「これ、もう作られてました」
最後に触ったのが、閉校予定や募集停止になっている学校の表示でした。全国で112校が、現役の学校とまったく同じ見た目でページに出ていました。中学3年生の家族がここに着地して志望校の候補に入れてしまったら、それはサービスとして失敗です。
アプリ側の詳細画面を直そうとしてコードを開いたら、既にちゃんと実装されていました。学校の状態(在校中、在校生のみ、閉校済みなど)と、募集の状態(募集中、募集終了、外部募集なし、など)を表示する仕組みがすでにあって、統合前の旧校名まで出せるようになっていました。
正直、拍子抜けしました。今日書くつもりだったコードの半分は、もう別の日の自分が書き終えていたわけです。結局やったのは、検索エンジン向けに用意している静的なページのほうに、その表示とまったく同じ文言を合わせて追加しただけでした。テキストだけしか見ないクローラーに、実際にアプリを開いた人と違う情報を見せるのは、それはそれでよくないので。

修正の前後でどう変わったか、図にするとこんな感じです。修正前は、リダイレクト先のページが「本物はリダイレクト元」と言い張っていて、修正後はリダイレクト先自身が正しく「自分が本物」と言えるようになりました。
小さく締める
朝は数字合わせのつもりで始めた確認作業が、気づいたら1日がかりのメンテナンスになっていました。直した内容はどれも地味です。スラッシュを1本足す、拡張子を1つ足す、既にある表示に文言を合わせる。派手さはないけれど、こういう積み重ねが後から効いてくるんだろうと思っています。
GSCの数字がちゃんと5,095から0に減るまでは、たぶん2〜6週間かかります。それまでは気長に、また別の粗を探すことにします。
※ ヘッダー画像とインフォグラフィックは AI(画像生成)で作成しています。
書いた人: ishizakahiroshi
群馬の北部で、保護猫2匹と暮らす、在宅エンジニア(何でも屋)
https://ishizakahiroshi.com/
https://github.com/ishizakahiroshi
X(業務委託・各種相談はこちら):
https://x.com/ishizakahiroshi
バックエンド・インフラ・AI連携まわりで、業務委託のご相談を受け付けています。フルリモートです。スポットや週2〜3時間からでも歓迎で、いろんな案件に携われたらうれしいです。こんな相談、歓迎です。
#Manabimap #個人開発 #SEO #サイトマップ #Web制作 #エンジニア #在宅ワーク #個人OSS #子育て #進路 #GoogleSearchConsole #デバッグ #振り返り
