本文へスキップ
オンラインショップ ショップ

サイトマップが2年間Googleに読まれていなかった話 ── 自社ECで起きたことと、原因のヘッダ3行

Blog
WEB
サイトマップが2年間Googleに読まれなかった話。クロールが止まった状態と正しく読み込まれた状態の対比イメージ

お恥ずかしい話から始めます。私たちが運営する自社のネットショップ(recross.shop)で、サイトマップが約2年間、Googleに読み込まれていませんでした。気づいたのは2026年8月20日。自社の失敗談ですが、同じ症状で困っているサイトはかなり多いはずなので、実際に測った数字をそのまま公開します。

何が起きていたか

Google Search Console(以下GSC)のサイトマップ画面で、「最終読み込み日時」が2024年8月25日で止まっていました。発見が2026年8月20日ですから、ちょうど2年間、Googleはサイトマップを読みに来ていなかったことになります。

その間に、ショップの商品は8,638件まで増えていました。ところがGSCの「検出されたページ数」は3,200件5,000件以上が、Googleから見えていなかったのです。

原因:サイトマップだけ、キャッシュ関連のヘッダを返していなかった

調べてみると、原因はシンプルでした。サイトマップのファイルが、更新の有無を伝えるHTTPヘッダを1つも返していなかったのです。トップページと比べると一目瞭然でした。

ヘッダ トップページ サイトマップ(修正前)
Last-Modified なし なし
ETag あり なし
Cache-Control あり なし

修正後は、次のようなヘッダを返すようにしました。

last-modified: Wed, 19 Aug 2026 00:50:26 GMT
etag: "c008b600519d3555173181d95f351ff9"
cache-control: public, max-age=3600

なぜ、それが致命的なのか

ここがこの記事の核心です。Googleはサイトマップを「毎回まるごと取得し直す」ことをしません。

クローラは「条件付きGET(conditional GET)」という仕組みを使います。

  1. 1回目:普通に取得する。このとき返ってきた Last-ModifiedETag を覚えておく
  2. 2回目:If-Modified-Since / If-None-Match を付けて「前と変わった?」と聞きに行く
  3. サーバが「変わっていない」と答えたら(304 Not Modified)、本文は送られない。転送量ゼロで済む

これはサーバにもGoogleにも優しい、よくできた仕組みです。ところが recross.shop のサイトマップは、そもそも Last-ModifiedETag も返していませんでした。

するとGoogleから見て、「このファイルは、更新されたかどうかを事前に判定する手段がない」という状態になります。中身が変わったか知るには、毎回すべてをダウンロードして比較するしかありません。商品サイトマップは9本あり、確認するだけで毎回3MB以上の転送が発生していました。

Googleはサイトごとに、クロールに使う資源(クロールバジェット)を配分しています。「毎回全部落とさないと変化が分からない、しかも落としてみたら大して変わっていない」ファイルは、優先度がじわじわ下がっていきます。下がりきった結果が、2年間の沈黙でした。

「サイトマップを送信した=Googleが見続けてくれる」ではありません。サイトマップは”置いてある”だけでは足りず、“更新されたと分かる形”で置く必要がある——これが今回いちばんの学びでした。

なぜ2年間も気づかなかったのか

ここも正直に書きます。気づけなかったのには、はっきりした理由がありました。

  • GSCのサイトマップ画面は、エラーを出しません。ステータスは「成功しました」のままでした
  • 「最終読み込み日時」は画面の右のほうに小さく表示されるだけで、その日付を毎回見る人はいません
  • サイトマップ自体は正常に生成されており、ブラウザで開けば中身も正しい
  • robots.txt も正しく、Googlebot を弾いてもいない

壊れていないものが、機能していなかった。これが、発見を2年遅らせた理由です。エラーが出ていれば、もっと早く気づけたはずでした。

どう直したか

サイトマップを出力しているのは Yoast SEO です。その出力の直前に割り込んで、先ほどのヘッダを足しました。使ったのは wpseo_sitemap_http_headers という正規のフックです。

add_action('wpseo_sitemap_http_headers', function ($headers) {
    $ts = rxshop_sitemap_lastmod();      // そのサイトマップの最終更新時刻
    if ($ts <= 0) return $headers;

    $last = gmdate('D, d M Y H:i:s', $ts) . ' GMT';
    $etag = '"' . md5($ts . '|' . get_query_var('sitemap') . '|' . (int) get_query_var('sitemap_n')) . '"';

    header('Last-Modified: ' . $last);
    header('ETag: ' . $etag);
    header('Cache-Control: public, max-age=3600');

    // 前回から変わっていなければ本文を送らない
    $ims = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
    $inm = trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? '');
    if (($ims !== '' && strtotime($ims) >= $ts) || ($inm !== '' && $inm === $etag)) {
        status_header(304);
        header('Content-Length: 0');
        exit;
    }
    return $headers;
}, 10, 1);

ポイントが2つあります。

  • 本文を1バイトでも出力したあとでは、header() は効きません。だからサイトマップ本文が出る前に差し込む必要があります。wpseo_sitemap_http_headers はそのための正規のフックです
  • Last-Modified の日時は、Yoastがサイトマップ内の <lastmod> に書いているのと同じ日時を使っています。XMLの中身とHTTPヘッダで別々の日付を返すと、かえって信用されません

結果

いつ 何が起きたか
8/20 修正を本番反映。索引18本すべてでヘッダを確認
8/20 GSCでサイトマップを再送信(手動で実施)
8/20 当日 読み込まれた。最終読み込み日時が2026年に更新
8/20 検出ページ数 3,200 → 8,955 に増加
8/20時点 インデックス登録済み 4,433件(8月初旬は約1,100件)

2年間動かなかったものが、実質ヘッダ3行で当日動きました。

ただし、正確に書いておきます。ヘッダの修正と再送信を同時にやっているため、どちらが効いたのかは厳密には分離できていません。「両方やったら、その日に読まれた」が事実です。ヘッダを付ければ必ず即日読まれる、という話ではありません。

「ページビルダーで作ったサイトだと起きやすい?」への答え

これはよく聞かれる問いですが、正直に切り分けます。

結論:直接の原因ではありません。ただし「見つかりにくくする」方向には効きます。

Last-Modified が出ていなかったのはサイトマップ出力側の仕様であって、ページビルダーのせいではありません。ページビルダーを使っていないサイトでも、同じことは起こります。ここは断定しません。

その上で、ページビルダーで作られたサイトが構造的に抱えやすい問題は確かにあり、自社ECで実測できたものを挙げます。

① ページビルダーは、やめたあとも本文に残る

当ショップでは、あるページビルダーはとっくに無効化しています。にもかかわらず、公開中の商品3,290件の抜粋欄に、そのビルダーのショートコードが文字列としてそのまま保存されていました。

WordPress は、登録されていないショートコードを、そのまま文字として画面に出します。つまり本来なら、3,290ページの商品説明に [○○-template id="171"] のような文字列が表示されるはずでした。

当ショップで表に出ていないのは、テーマ側で同名のショートコードを引き継いで実装し、正しい中身を出す受け皿を意図的に用意したからです。気づいて手を打ったから助かっただけで、気づかなければ3,290ページが壊れて表示されていました。そして商品が8,638件もあると、「商品ページの文章がおかしい」ことに気づく人は誰もいません。

ページビルダーは、プラグインを消してもデータベースに書かれたショートコードまでは消えません。「やめたはずなのに、本文の中に残り続ける」——これは移行時にいちばん見落とされる点です。

② 検索結果に、レイアウト用の記述が露出していた

ビルダー時代に作られた商品ページでは、抜粋欄にレイアウト用の記述が混ざっており、Googleの検索結果のスニペットに、その断片がそのまま表示されていました。

説明文(description)が正しく出ていなかったページは、修正前で6,306件、修正後は0件。GSCの実績は表示回数14,200/クリック95/CTR 0.7%/平均掲載順位4.6位でした。順位は取れているのに、クリックされない。その主因が、説明文の欠落だったわけです。

(余談ですが、これは「meta keywords は効かない。効くのは title・description・構造化データ」という原則そのままの事例でもあります。上位に出ていても、説明文が壊れていればクリックされません。)

③ 表示が重い

ページビルダーは、要素ごとに div とインラインの装飾を積み上げていくため、HTMLが膨らみがちです。実際、当ショップのトップページはHTML本体だけで216.7KB(一般的な目安は50〜100KB)、外部CSS7本・外部JS18本という状態でした。クロールバジェットは「回数」だけでなく「重さ」でも消費されます。

ただし正確に書くと、現在のトップページのHTMLに、そのビルダー由来の記述はすでに一切残っていません(自前テーマに移行済みのため)。「いまも重くしている」わけではない点は補足しておきます。

④ サイトマップは、放っておくと勝手に太る

現在の索引に含まれる18本のうち、3本(投稿者アーカイブ/注文状況ページ/メルマガ用ページ)はそもそも検索結果に出したいページではありません。これはビルダーのせいではなく、プラグインを入れるたびに投稿タイプが増え、サイトマップ生成側がそれを自動で拾い続けた結果です。

サイトマップは、放っておくと太ります。プラグインを入れた本人も、それがサイトマップに載っていることを知りません。いつのまにか「クロールしてほしいページ」ではなく「たまたま公開されているページ」の一覧になっていきます。(この3本の除外は、これから対応します。)

※ 上記のうち「保存のたびに更新扱いになりクロール頻度が下がる」「リクエスト数が多く応答が遅くなる」といった点は、筋は通りますが自社では証明しきれていないため、あくまで可能性としてお読みください。

今すぐできる、4つの自己点検

専門的な話が続きましたが、持ち帰っていただきたいのは次の4点だけです。

  1. GSCで最終読み込み日時を見る。「サイトマップ」画面の日付が1年以上前で止まっていたら、同じ症状の可能性があります
  2. レスポンスヘッダを確かめる。ブラウザの開発者ツールでサイトマップのURLを開き、Last-Modified があるか見る。無ければ黄信号です
  3. サイトマップの中身を年1回は見る。プラグインを入れるたびに、意図しないページが載っていきます
  4. ページビルダーは、やめたあとも残る。本文に書き込まれたショートコードは、プラグインを消しても消えません

なぜ、自社の失敗を公開するのか

自社の失敗を出すのは、正直に言えば気が引けます。それでも公開するのは、GSCがエラーを出さない以上、同じ状態のまま気づいていないサイトが必ず他にもあると思うからです。この記事で1サイトでも救えるなら、恥をかく価値はあります。

私たちリクロスは、印刷・印章の会社であると同時に、こうした自社ECの技術的な問題を、自分たちの手で見つけて直しています。ホームページの制作・保守も、同じ目線で——「作って終わり」ではなく、検索に正しく載り、クリックされ、成果につながるところまで——お手伝いしています。同じような不安がある方は、お気軽にご相談ください。