ログオフ
開発2026-08-11

Search Console で「合っているように見えて違う」3箇所:APIの並び順・修正を検証・robots.txt

APIで取った上位クエリが管理画面と一致しない。「修正を検証」が何度押しても失敗する。robots.txt に Disallow を足したら sitemap と矛盾する。どれもエラーが出ないので、間違ったまま運用が進む。

Search Console は、間違った使い方をしても警告を出さない。数字はきれいに出るし、ボタンも押せる。狂うのは判断だけだ。順に書く。

1. APIの「上位クエリ」は、自分で並べ替えないと嘘になる

事象

Search Analytics API で検索クエリを取り、「表示回数の多いクエリ Top5」を毎月出すスクリプトを書いた。合計値は管理画面と一致する。ところがクエリ一覧だけが、管理画面とまったく違うものを出していた。

数字で言うと、本当の1位は30表示のクエリだったのに、6表示のクエリが1位として印字されていた。

原因

Search Analytics API には「表示回数で並べる」という指定が無い。公式ドキュメントにはこう書いてある。

Results are sorted by click count descending. If two rows have the same click count, they are sorted in an arbitrary way.

返ってくる行は常にクリック数の降順で、クリック数が同じ行の並びは不定。多くのサイトではクエリの大半がクリック0なので、実質「クリック0の行が不定の順で並ぶ」ことになる。

そこへ rowLimit: 5 を渡すと、その不定の並びの先頭5件が返る。表示回数とは何の関係も無い5件が「表示上位クエリ」として出力される。

気づきにくい理由が2つある。ひとつは合計値が正しいままであること。合計は dimensions(クエリ別・ページ別といった内訳の指定)を渡さずに取るので、この並び順の問題を踏まない。検算しても合うので、疑う理由が生まれない。もうひとつは、出てきた一覧がそれらしく見えること。エラーも警告も出ず、実在するクエリと実在する数字が並ぶ。壊れている手がかりがゼロなので、管理画面と突き合わせて初めてわかる。

// ❌ 上位5件のつもりが、クリック0の行の先頭5件
const res = await searchconsole.searchanalytics.query({
  siteUrl: 'https://example.com/',
  requestBody: { startDate, endDate, dimensions: ['query'], rowLimit: 5 },
})
const top5 = res.data.rows   // 表示回数順ではない

解決策

多めに取って、クライアント側で並べ替える。rowLimit は最大 25,000、既定は 1,000。足りなければ startRow でページングする。

// ✅ 多めに取ってから自分でソートする
const res = await searchconsole.searchanalytics.query({
  siteUrl: 'https://example.com/',
  requestBody: { startDate, endDate, dimensions: ['query'], rowLimit: 1000 },
})
const top5 = (res.data.rows ?? [])
  .sort((a, b) => b.impressions - a.impressions)
  .slice(0, 5)

一般化すると、「上位N件」を返すAPIは、何で並んでいるかをドキュメントで確認してから使う。rowLimitlimit は「並べ替えてから上位N件」ではなく「APIが決めた順序の先頭N件」であることが多い。件数を絞る指定が、暗黙にソート条件を兼ねてしまう。

2. 「修正を検証」は、404 では押しても通らない

事象

ページのインデックス登録レポートに「サーバーエラー(5xx)」が3桁件たまっていた。前回クロールを見ると2ヶ月近く前で止まっている。調べたらコード側の不具合はすでに直っていて、残っていたのは Google の記録だけだった。

同じレポートには「見つかりませんでした(404)」も出ていた。こちらはリンク元を直したので「修正を検証」を押した。失敗した。

原因

「修正を検証」は、対象URLを再クロールして、そのURLの現在の応答を見る。ドキュメントでは、問題が解決したと判定されるのは「URLがクロールされ、そのページで問題が見つからなくなったとき」とされている。

ここで404と5xxの性質が分かれる。

5xx なら、サーバーが直っていれば実際に200が返るので検証は通る。404 は違う。リンク元を直しても、URL自体は404を返し続ける。そもそも絞り込み結果が0件のページのように、404が正しい応答であることもある。この状態で押しても、Google が見る応答は何も変わらない。

直した側からすると「もう直した」つもりなので、失敗の理由がわかりにくい。

解決策

押す前に、そのURLの応答を実測する。1行で判別できる。

curl -s -o /dev/null -w "%{http_code}\n" https://example.com/some-path

200が返るなら押す。404が返るなら押さない。404を消したいなら、リンク元を直すのとは別の話になる。そのURLを200にするか、リンクが消えた状態で自然に落ちるのを待つことになる。

もう一段の注意として、5xxを放置すると Google はクロールの頻度そのものを落とす。500・503・429 が続くと自動的にクロールが減り、エラーが減れば自動的に戻る、と明記されている。だから5xxは直しても即座には回復せず、インデックス登録済みのページ数が落ちたまま張り付く。登録済みが減っていたら、表示回数の増減を眺めていても原因には辿り着けない。未登録の理由の内訳を開くほうが早い。

3. robots.txt の Disallow は、sitemap と noindex を道連れにする

事象

DBを直に叩く動的ページ(Next.js でいう force-dynamic)をクローラーが大量に踏み、DBの外部転送量が無料枠の7倍に張り付いた。対策として robots.txt に Disallow: /foo/ を足した。これだけでは片手落ちになる。

原因

別々の罠が3つ同時に起きる。

罠1。sitemap が同じURLを載せたままだと、Search Console が「sitemap に含まれる URL が robots.txt でブロックされています」を出す。ブロック(robots.txt)と宣伝(sitemap)と索引可否(meta robots タグ)は別のレイヤーで、3つを揃えないと整合しない。

罠2。Disallow は前方一致だ。公式ドキュメントも /fish について「/fish で始まるすべてのパスに一致する」と書いている。詳細ページだけ止めるつもりで /foo/ を書くと、/foo/ranking のような配下の別ページまで巻き込む。

罠3。進行中のエラー検証が終わらなくなる。5xx などで「修正を検証」が走っている最中にそのディレクトリを robots.txt で塞ぐと、再クロールが来ないので検証はいつまでも「開始」のまま止まる。個別にインデックス登録をリクエストしても通らない。

解決策

塞ぐ前に、目的が「索引から外したい」のか「踏まれたくない」のかを決める。打ち手が変わる。

目的打ち手
転送量・負荷を減らしたいDisallow でクロールを止める。同時に sitemap からも外す
検索結果から消したいクロールは許して noindex を読ませる。Disallow は使わない

索引から外したいときに Disallow を使うのが一番まずい。Google はブロックしたページの中身を読めないので、そこに書いた noindex にも到達できない。ドキュメントにも、クロールを禁止されたページは内容をインデックスできないがURL自体は検索結果に出ることがある、と書かれている。つまり消したくてブロックすると、かえって消えなくなる

塞ぐ前にもうひとつ、そのディレクトリに未解決の検証が残っていないかを見ておくと、罠3で悩まずに済む。

効くパターン:合計ではなく内訳を、別経路と突き合わせる

3つとも「エラーが出ない」で共通している。特に1番は、合計値の検算では永久に見つからなかった。見つかった唯一の経路は、スクリプトの出力と管理画面を並べて見たことだった。

自動で数字を出す仕組みを作ったら、合計だけでなく内訳の先頭数件を、別経路で取った値と突き合わせる定点観測を持っておく。月1回、1分で済む。合計が合っているうちは、内訳が壊れていても誰も気づかない。

関連記事

開発 一覧へ