本文へスキップ
試したこと INDEXNOW

WordPress に IndexNow をプラグインなしで入れてみました

Bing IndexNow PHP WordPress
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 つです。

  1. キーを初回に自動で作って option に保存する
  2. /キー.txt を WordPress 側で返す
  3. 記事・制作物・固定ページの公開、更新、非公開のたびに、その URL と関係する一覧ページを送る
  4. 全 URL の一括送信を関数として持ち、管理画面を開いたときに 1 度だけ走らせる

キーファイルは、本物のファイルを置かずに済ませました。nginx の try_files が存在しないパスを index.php に回すので、WordPress の init でリクエストのパスを見て、キーと同じなら text/plain で返しています。

terminal
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 件だけ残しています。

terminal
$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 を組み立てました。

terminal
$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 ページのままにしておく理由は無いと思います。

share Xでシェア