2026年5月21日

2026年5月21日

WordPressのスパム対策を強化する方法|コメント・フォーム・ユーザー登録を経路別に守る

WordPressのスパム対策は、「どこから来ているか」を経路ごとに切り分け、標準設定・判定サービス・ハニーポット・サーバー側の制限を重ねるのが効果的です。この記事では、コメント、お問い合わせフォーム、ユーザー登録、ピンバックの4つの経路について、設定の手順と、正規の利用者を巻き込まないための注意点、効いているかの確認方法を解説します。

まず「どの経路のスパムか」を切り分ける

対策を足す前に、スパムがどこから入っているかを確かめます。経路によって効く対策がまったく違うためです。

  • コメント: 管理画面の「コメント」に大量の承認待ち・スパムが溜まる
  • お問い合わせフォーム: フォームから届くメールに意味不明な文面や宣伝リンクが並ぶ
  • ユーザー登録: 「ユーザー」一覧に見覚えのないアカウントが増える
  • ピンバック・トラックバック: コメント欄に「〜より」形式の通知が大量に届く

サーバーのアクセスログで、どのファイルへのPOSTが多いかを見るのが手っ取り早い方法です。コメントは wp-comments-post.php、ユーザー登録は wp-login.php?action=register、ピンバックは xmlrpc.php への送信として記録されます。

# POST先ごとの件数(Nginxの例。ログのパスは環境に合わせて変更)
sudo awk '$6 ~ /POST/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

コメントスパム: 標準のディスカッション設定を見直す

WordPressの「設定」→「ディスカッション」には、プラグインなしで使えるスパム対策がまとまっています。最初にここを整えると、後で入れる判定サービスの負担も減ります。

  • コメントの手動承認: すべてのコメントを承認制にする設定です。小規模なブログならこれだけでスパムが表に出ることはなくなります。
  • リンク数による保留: 「コメント内に指定数以上のリンクがあれば承認待ちにする」という項目です。スパムの多くは複数のURLを含むため、1〜2に下げると効果があります。
  • コメントモデレーションの語句リスト: ここに書いた語句・URL・IPアドレスを含むコメントは承認待ちに回ります。
  • コメント禁止キー(Disallowed Comment Keys): こちらに書いた語句を含むコメントは承認待ちを経ずにゴミ箱へ送られます(WordPress 5.5より前はスパム扱いでした)。誤判定の影響が大きいので、明らかに業者と分かる語句だけに絞ります。
  • 古い記事のコメントを自動的に閉じる: スパムは検索で見つけやすい古い記事に集中しがちです。議論が終わった記事は日数を決めて閉じると、受付面積そのものが減ります。

コメント機能自体が不要なサイトなら、「新しい投稿へのコメントを許可する」をオフにし、既存記事も閉じてしまうのが一番確実な方法です。既存記事を一括で閉じるにはWP-CLIが使えます。実行前にデータベースのバックアップを取ってください。

# 公開済み投稿のコメント受付を一括で閉じる
wp post list --post_type=post --post_status=publish --format=ids \
  | xargs -n 50 wp post update --comment_status=closed --ping_status=closed

判定サービスを入れる: AkismetとAntispam Bee

標準設定で数を減らしたうえで、残りを自動判定に任せます。代表的なのは次の2つです。

Akismet はコメントの内容・送信元の情報を外部のサービスに送り、スパムかどうかを判定させる方式です。判定精度が高い一方、APIキーが必要で、個人の非商用サイトと商用サイトで利用条件が異なります(2026年10月現在。最新の条件は公式サイトで確認してください)。コメントデータを外部に送信するため、プライバシーポリシーへの記載も検討します。

Antispam Bee は外部サービスへのデータ送信なしで動く判定プラグインです。ハニーポット、送信元の言語・国による判定、過去のスパムとの照合などを組み合わせます。外部送信を避けたい場合の選択肢になります。

どちらを使う場合も、導入後しばらくは「スパム」に振り分けられたコメントを週に1回程度確認し、正規のコメントが混ざっていないかを見てください。誤判定に気付いたら「スパムではない」として戻すと、判定の改善にもつながります。

自前で入れるハニーポットと送信時間チェック

ボットは画面に見えない入力欄にも値を入れる傾向があります。これを利用するのがハニーポットです。あわせて「フォーム表示から送信までが極端に短いもの」を弾くと、単純なボットの多くを止められます。

以下はテーマの functions.php か自作プラグインに置くコード例です。送信時刻は改ざんを防ぐため wp_hash() で署名し、受け取った値はサニタイズしてから比較しています。

<?php
// コメントフォームにハニーポット欄と署名付きタイムスタンプを出力する
add_action( 'comment_form', function () {
	$time = time();
	$sig  = wp_hash( 'wpdr_comment_time|' . $time );
	echo '<p style="position:absolute;left:-9999px;" aria-hidden="true">';
	echo '<label for="wpdr_hp">この欄は空のままにしてください</label>';
	echo '<input type="text" name="wpdr_hp" id="wpdr_hp" value="" tabindex="-1" autocomplete="off">';
	echo '</p>';
	echo '<input type="hidden" name="wpdr_ts" value="' . esc_attr( $time . '|' . $sig ) . '">';
} );

// 送信時に検査する(ログイン中のユーザーは対象外)
add_filter( 'preprocess_comment', function ( $commentdata ) {
	if ( is_user_logged_in() ) {
		return $commentdata;
	}
	// ピンバック・トラックバックはフォームを通らないので対象外
	if ( ! empty( $commentdata['comment_type'] ) && in_array( $commentdata['comment_type'], array( 'pingback', 'trackback' ), true ) ) {
		return $commentdata;
	}

	$hp = isset( $_POST['wpdr_hp'] ) ? sanitize_text_field( wp_unslash( $_POST['wpdr_hp'] ) ) : '';
	if ( '' !== $hp ) {
		wp_die( esc_html__( 'コメントを送信できませんでした。' ), '', array( 'response' => 403, 'back_link' => true ) );
	}

	$ts    = isset( $_POST['wpdr_ts'] ) ? sanitize_text_field( wp_unslash( $_POST['wpdr_ts'] ) ) : '';
	$parts = explode( '|', $ts );
	$valid = 2 === count( $parts )
		&& hash_equals( wp_hash( 'wpdr_comment_time|' . $parts[0] ), $parts[1] );

	// 署名が不正、または表示から3秒未満の送信は拒否
	if ( ! $valid || ( time() - (int) $parts[0] ) < 3 ) {
		wp_die( esc_html__( '送信が早すぎるか、フォームが正しくありません。ページを再読み込みしてやり直してください。' ), '', array( 'response' => 403, 'back_link' => true ) );
	}

	return $commentdata;
} );

ここで「上限の経過時間」を設けていないのは、ページキャッシュを使っているサイトではフォームのHTMLが長時間使い回され、タイムスタンプが古くなるためです。同じ理由で、ログアウト状態のコメントフォームにnonceを追加して検証するのも避けます。キャッシュされたページのnonceは期限切れになり、正規の訪問者のコメントまで拒否してしまうからです。WordPress本体がログアウト時のコメントフォームにnonceを付けていないのはこのためです。

また、スパム対策のコードを wp-login.php やログイン処理(authenticate フィルターなど)に安易に差し込むのは避けてください。検証の条件を誤ると管理者自身がログインできなくなります。ログイン画面の保護は、スパム対策とは分けて設計するのが安全です。

確認方法: ログアウトした状態(別ブラウザやプライベートウィンドウ)で実際にコメントを投稿し、正常に受け付けられることを確かめます。表示直後に送信した場合や、開発者ツールでハニーポット欄に値を入れた場合に拒否されれば動作しています。

お問い合わせフォームのスパム対策

フォームプラグインの多くは、CAPTCHAや判定サービスとの連携機能を持っています。Contact Form 7を例にすると、管理画面の「お問い合わせ」→「インテグレーション」からreCAPTCHA(v3)などの連携を設定できます。対応しているサービスはバージョンによって異なるため、2026年10月現在の画面で確認してください。

reCAPTCHA v3は利用者に画像選択などを求めず、操作の自然さをスコア(0.0〜1.0)で評価します。Contact Form 7ではしきい値を wpcf7_recaptcha_threshold フィルターで変更できます。しきい値を上げるほど厳しくなりますが、正規の問い合わせが弾かれる可能性も上がるため、まず既定値で様子を見ます。

<?php
// reCAPTCHA v3 のスコアしきい値を 0.5 から 0.6 に引き上げる
add_filter( 'wpcf7_recaptcha_threshold', function ( $threshold ) {
	return 0.6;
} );

ほかにも、Contact Form 7ではフォームタグにAkismet連携のオプション(akismet:author など)を付けて送信内容をAkismetで判定させることもできます。外部サービスを使いたくない場合は、ハニーポット欄を追加する拡張プラグインや、簡単な質問に答えさせるクイズ形式のタグが候補になります。

フォームの対策で見落としやすいのが、自動返信メールです。スパムボットが他人のメールアドレスを入力して送信すると、自動返信がその第三者に届き、サイトがスパム送信元のように扱われることがあります。自動返信に本文をそのまま引用しない、または自動返信自体を止めることも検討してください。

ユーザー登録スパムを止める

会員機能を使っていないのに見知らぬユーザーが増えている場合、「設定」→「一般」の「だれでもユーザー登録ができるようにする」がオンになっていないかを確認します。不要ならオフにするだけで登録スパムは止まります。あわせて「新規ユーザーのデフォルト権限グループ」が「購読者」になっていることも確かめてください。ここが編集者や管理者になっていると、スパムどころか乗っ取りの入口になります。

登録を受け付ける必要がある場合は、登録フォームにもハニーポットを入れられます。register_form で欄を出力し、registration_errors でエラーを返すのが標準的な方法です。ログイン処理には触れないので、既存ユーザーのログインに影響しません。

<?php
add_action( 'register_form', function () {
	echo '<p style="position:absolute;left:-9999px;" aria-hidden="true">';
	echo '<input type="text" name="wpdr_reg_hp" value="" tabindex="-1" autocomplete="off">';
	echo '</p>';
} );

add_filter( 'registration_errors', function ( $errors ) {
	$hp = isset( $_POST['wpdr_reg_hp'] ) ? sanitize_text_field( wp_unslash( $_POST['wpdr_reg_hp'] ) ) : '';
	if ( '' !== $hp ) {
		$errors->add( 'wpdr_spam', __( '登録できませんでした。' ) );
	}
	return $errors;
} );

WooCommerceなど独自の登録画面を持つプラグインを使っている場合、この2つのフックは効きません。そのプラグイン側の設定やフックで対策します。

ピンバック・トラックバックを止める

ピンバックとトラックバックは現在ほとんど正規の用途がなく、スパムの経路になっていることが大半です。「設定」→「ディスカッション」で他のブログからの通知(ピンバック・トラックバック)の受け付けをオフにし、既存記事も前述のWP-CLIで ping_status を閉じます。XML-RPCのピンバック機能だけを無効にするなら、次のコードが使えます。

<?php
add_filter( 'xmlrpc_methods', function ( $methods ) {
	unset( $methods['pingback.ping'], $methods['pingback.extensions.getPingbacks'] );
	return $methods;
} );

サーバー側で送信回数を制限する

ボットが短時間に大量送信してくる場合は、PHPに届く前にWebサーバーで回数を絞るとサーバー負荷も下がります。Nginxなら limit_req を使います。location を新たに作ると既存のPHP処理の設定が適用されなくなるため、中には今使っているPHP-FPMへの受け渡し設定と同じ内容を書く必要があります。設定変更の前には現在のファイルを複製しておき、nginx -t で構文を確認してから反映してください。

# http ブロック内: 送信元IPごとに1分あたり5回まで
limit_req_zone $binary_remote_addr zone=wpcomment:10m rate=5r/m;

# server ブロック内
location = /wp-comments-post.php {
    limit_req zone=wpcomment burst=3 nodelay;
    limit_req_status 429;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php-fpm.sock;  # 既存の設定に合わせる
}

CDNやリバースプロキシの背後にあるサーバーでは、$binary_remote_addr がプロキシのIPになり、全訪問者がまとめて制限されてしまいます。その場合は real_ip 関連の設定で実際の送信元IPを取得できるようにしてから導入します。

溜まったスパムを片付ける

既に大量のスパムが溜まっている場合は、WP-CLIでまとめて削除できます。--force を付けるとゴミ箱を経由せず即座に削除されるため、実行前にデータベースのバックアップを取ってください。

# スパム判定済みのコメント件数を確認
wp comment list --status=spam --format=count

# スパム判定済みのコメントを削除(ゴミ箱を経由しない)
wp comment delete $(wp comment list --status=spam --format=ids) --force

承認待ちの中に正規のコメントが混ざっている可能性がある場合は、承認待ち(--status=hold)を一括削除するのは避け、内容を見ながら処理します。

まとめ

  • 最初にアクセスログなどでスパムの経路(コメント・フォーム・登録・ピンバック)を切り分ける
  • コメントはディスカッション設定(リンク数保留・語句リスト・古い記事のコメント終了)を整え、AkismetやAntispam Beeで判定する
  • ハニーポットと送信時間チェックは効果が高いが、ページキャッシュとの相性を考えてnonceや上限時間は使わない
  • お問い合わせフォームはプラグインのCAPTCHA連携を使い、自動返信が第三者への迷惑メールにならないか確認する
  • 不要なユーザー登録とピンバックは止め、ログイン処理には手を入れない
  • 大量送信にはWebサーバーの回数制限が有効。プロキシ配下では実際の送信元IPの取得を先に整える

お気軽にご相談ください

お見積りへ お問い合わせへ
▲