WordPress に IndexNow をプラグインなしで入れてみました
Bing にこのサイトが 1 ページも載っていなかったので、IndexNow をテーマの 1 ファイルで入れました。キーの配り方、公開のたびに送る仕組み、22 件を送って HTTP 202 が返るまでを、コードと一緒に書きます。
Google にインデックスされない件を調べていて、ついでに Bing を見たら 0 ページでした。site:siam-works.com で何も出ません。アクセスログを見ると、bingbot が読んでいたのは robots.txt とトップページだけでした。
Bing には IndexNow という、URL を直接知らせる仕組みがあります。プラグインを入れれば済む話ですが、このサイトはプラグインを使わない方針なので、テーマに 1 ファイル足して自分で送ることにしました。仕組みと、送ってみた結果を書きます。
IndexNow は何をするものなのか
IndexNow は、サイト側から「この URL が増えた・変わった・消えた」を検索エンジンに知らせる API です。Bing、Yandex、Naver などが参加していて、1 か所に送れば参加エンジンに共有されます。Google は参加していないので、Google 向けは Search Console から別にやります。
必要なのは 3 つです。
- 英数字 8〜128 文字のキー
- そのキーを書いたテキストファイルを、
https://サイト/キー.txtで配信すること(送信元が本当にそのサイトか確かめるため) https://api.indexnow.org/indexnowに、ホスト名・キー・URL の一覧を JSON で POST すること
送れる URL は 1 回に 10,000 件まで。受け付けられると 200 か 202 が返ります。
仕組みはこれだけでした
テーマに inc/indexnow.php を 1 つ足し、functions.php から読み込んでいます。やっていることは 4 つです。
- キーを初回に自動で作って option に保存する
/キー.txtを WordPress 側で返す- 記事・制作物・固定ページの公開、更新、非公開のたびに、その URL と関係する一覧ページを送る
- 全 URL の一括送信を関数として持ち、管理画面を開いたときに 1 度だけ走らせる
キーファイルは、本物のファイルを置かずに済ませました。nginx の try_files が存在しないパスを index.php に回すので、WordPress の init でリクエストのパスを見て、キーと同じなら text/plain で返しています。
function siamworks_indexnow_serve_key() {
$path = (string) wp_parse_url( (string) $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$want = (string) wp_parse_url( siamworks_indexnow_key_url(), PHP_URL_PATH );
if ( '' === $want || $path !== $want ) {
return;
}
status_header( 200 );
nocache_headers();
header( 'Content-Type: text/plain; charset=utf-8' );
header( 'X-Robots-Tag: noindex' );
echo siamworks_indexnow_key();
exit;
}
add_action( 'init', 'siamworks_indexnow_serve_key', 1 );送信は wp_remote_post で JSON を投げるだけです。このサイトのホスト以外の URL(制作物の公開先が外部のとき)は捨てて、結果を option に 20 件だけ残しています。
$body = array(
'host' => $host,
'key' => siamworks_indexnow_key(),
'keyLocation' => siamworks_indexnow_key_url(),
'urlList' => $urls,
);
$res = wp_remote_post(
'https://api.indexnow.org/indexnow',
array(
'timeout' => 8,
'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
'body' => wp_json_encode( $body ),
)
);公開のたびに送る部分は transition_post_status に付けています。新旧どちらかの状態が publish のときだけ送るので、下書きの保存では動きません。
詰まったところ
ゴミ箱に入れた記事の URL です。WordPress は記事をゴミ箱に入れるとスラッグの末尾に __trashed を足すので、そのまま get_permalink() を呼ぶと、存在しなかった URL を送ってしまいます。元のスラッグに戻した複製を作ってから URL を組み立てました。
$tmp = clone $post;
$tmp->post_status = 'publish';
$tmp->post_name = preg_replace( '/__trashed(-\d+)?$/', '', (string) $post->post_name );もう 1 つは、ローカルの開発環境から本番の URL を送ってしまわないことです。ホスト名が本番のときだけ送り、それ以外はログに「skipped」と残すようにしました。
送ってみた結果
本番で全 URL の一括送信を走らせると、22 件を 1 回の POST で送って HTTP 202 が返りました。トップ、記事一覧、制作物一覧、記事 9 本、固定ページ 2 本、カテゴリ 4 本、ゲームのページ 4 本です。
受け付けたという意味であって、載ったという意味ではありません。載ったかどうかは Bing Webmaster Tools で見ます。サイトを手動で追加して、ルートに置いた BingSiteAuth.xml で所有権を確認し、サイトマップを送るところまでやりました。登録のときに「WordPress のログイン情報で確認する」という案内が出ますが、確認はファイルを置くだけで済むので、パスワードを渡す必要はありません。
コードは Claude Code に書いてもらいました。私が決めたのは、プラグインを入れないこと、キーファイルを実体のファイルにしないこと、本番以外から送らないことの 3 つです。
Bing は日本の検索の数%ですが、ChatGPT の検索は Bing の索引を使います。0 ページのままにしておく理由は無いと思います。