2026年8月3日
2026年8月3日
WordPressで不正ログイン試行をリアルタイムで遮断するWAF設定(Cloudflare・nginx・fail2ban・プラグイン)
WordPress への不正ログイン試行をリアルタイムで止めるには、ログイン画面(wp-login.php)と xmlrpc.php へのリクエストを、PHP が動く前の段階でできるだけ手前の層(CDN の WAF、Web サーバー、ファイアウォール)で弾き、最後の守りとしてプラグインのログイン制限を重ねるのが効果的です。この記事では、Cloudflare の WAF ルール、nginx のレート制限、fail2ban による自動遮断、セキュリティプラグインの4つの層について、それぞれの設定例と、自分を締め出さないための注意点を説明します。
ブルートフォース攻撃全般の対策(強いパスワード、2段階認証、ユーザー名の露出を減らすなど)は前提として済ませておき、この記事ではその上に「遮断の仕組み」を足すことに絞ります。
どの層で止めるかを先に決める
不正ログインの遮断は、どこで処理するかによって負荷と効果が大きく変わります。
- CDN・クラウド WAF(Cloudflare など): リクエストがサーバーに届く前に止まるため、サーバー負荷がもっとも小さくなります。DNS を CDN 経由にしている必要があります。
- Web サーバー(nginx の limit_req、Apache の ModSecurity など): PHP を起動せずに 429 や 403 を返せるため、大量の試行でも PHP-FPM のプロセスが埋まりにくくなります。
- OS のファイアウォール(fail2ban+iptables/nftables): ログを監視して、失敗を繰り返した IP アドレスを一定時間まるごと遮断します。
- WordPress プラグイン(Wordfence、Limit Login Attempts Reloaded など): ユーザー名や失敗回数など WordPress の文脈を使った判定ができますが、PHP と WordPress が起動してから動くため、攻撃量が多いとサーバーへの負担は残ります。
レンタルサーバーで root 権限がない場合は、CDN とプラグインの組み合わせが現実的です。VPS なら4層すべてを使えます。どの構成でも、まず「自分の IP を許可リストに入れる」「止まったときに解除する方法を知っておく」の2点を用意してから設定してください。
Cloudflare の WAF ルールで wp-login.php を守る
Cloudflare を使っている場合、ダッシュボードのセキュリティ関連の画面から、カスタムルールやレート制限ルールを作成できます。メニュー名や無料プランで使えるルール数は変更されることがあるため、作成時に Cloudflare のドキュメントで確認してください(2026年10月現在)。
カスタムルールでは、ログイン画面への POST に対してチャレンジ(Managed Challenge など)をかけるのが扱いやすい方法です。人間はブラウザ上でチャレンジを通過できますが、単純なスクリプトはここで止まります。
# カスタムルールの式の例(ログインへのPOSTとXML-RPC、自分のIPは除外)
(
(http.request.uri.path contains "/wp-login.php" and http.request.method eq "POST")
or http.request.uri.path contains "/xmlrpc.php"
)
and not ip.src in {203.0.113.10}
レート制限ルールでは「同じ IP から wp-login.php への POST が一定時間内に一定回数を超えたらブロック」という形で設定します。最初は回数を緩めに設定し、正規の利用者が引っかからないことを確認してから絞るのが安全です。
Cloudflare 経由にした場合、サーバー側のログに残る送信元は Cloudflare の IP アドレスになります。後述の nginx のレート制限や fail2ban を併用するときは、nginx の set_real_ip_from と real_ip_header CF-Connecting-IP で本当の送信元を記録できるようにしておく必要があります。また、Cloudflare 経由の通信はサーバーから見ると Cloudflare の IP から届くため、iptables で攻撃者の IP を遮断しても効きません。Cloudflare を通している構成では、遮断は Cloudflare 側で行うのが基本です。
nginx の limit_req でログイン POST を制限する
nginx を使っている VPS なら、limit_req で wp-login.php への POST の頻度を制限できます。GET(ログイン画面の表示)は制限せず、POST(ログイン送信)だけを数えるために map を使います。キーが空文字のリクエストはカウントされないという nginx の仕様を利用した書き方です。
# http ブロック内
map $request_method $wplogin_limit_key {
default "";
POST $binary_remote_addr;
}
limit_req_zone $wplogin_limit_key zone=wplogin:10m rate=6r/m;
# server ブロック内(既存の PHP 設定に合わせて fastcgi 部分を書く)
location = /wp-login.php {
limit_req zone=wplogin 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;
}
この例では、同じ IP からの POST を平均10秒に1回(1分あたり6回)のペースに制限し、短時間の連続は3回まで余分に受け付け、それを超えた分には 429 を返します。location = /wp-login.php を作ると、既存の location ~ \.php$ よりこちらが優先されるため、fastcgi の設定を書き忘れると PHP が実行されずにファイルがダウンロードされる、ログインできないといった不具合になります。既存の PHP 用 location の中身をそのまま写すと設定漏れを防げます。
設定後は nginx -t で文法を確認してから systemctl reload nginx で反映します。XML-RPC を使っていない場合(Jetpack や一部のアプリ連携では使われます)は、location = /xmlrpc.php { deny all; } で閉じてしまうのも有効です。
fail2ban で失敗を繰り返す IP を自動遮断する
fail2ban は、ログファイルを監視して条件に合う IP をファイアウォールで一定時間遮断するツールです。WordPress のログインは、失敗すると 200(ログイン画面の再表示)、成功すると 302(管理画面へのリダイレクト)を返すため、アクセスログの「wp-login.php への POST が 200 で返った回数」を失敗として数えられます。
# /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
failregex = ^<HOST> -.*"POST /wp-login\.php HTTP/[0-9.]+" 200
^<HOST> -.*"POST /xmlrpc\.php HTTP/[0-9.]+" 200
ignoreregex =
# /etc/fail2ban/jail.d/wordpress-login.local
[wordpress-login]
enabled = true
port = http,https
filter = wordpress-login
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600
ignoreip = 127.0.0.1/8 203.0.113.10
この設定は「10分(600秒)以内に5回失敗した IP を1時間遮断する」という意味です。ignoreip には自分のオフィスや自宅の固定 IP を入れておきます。フィルターが実際のログに合っているかは、反映前に次のコマンドで確認できます。
# フィルターがログの何行にマッチするかを確認
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress-login.conf
# 反映と状態確認
systemctl restart fail2ban
fail2ban-client status wordpress-login
# 誤って遮断したIPの解除
fail2ban-client set wordpress-login unbanip 203.0.113.10
ログの形式は nginx の log_format の設定で変わるため、独自形式を使っている場合は正規表現を合わせて調整してください。xmlrpc.php は成功時にも 200 を返すため、XML-RPC を正規に使っている環境では2行目を外すか、利用元の IP を ignoreip に入れます。
プラグインで最後の守りを置く
サーバー側の対策に加えて、WordPress 側でもログイン失敗の回数制限を入れておくと、ユーザー名単位の判定や管理画面での状況確認ができます。代表的なのは次のプラグインです。
- Wordfence Security: アプリケーション層のファイアウォールとログイン保護(失敗回数による一時ロック、2段階認証など)を持ちます。ファイアウォールを WordPress より先に読み込ませる最適化の設定があり、有効にすると
auto_prepend_fileを使う構成になります。 - Limit Login Attempts Reloaded: ログイン失敗の回数制限に絞ったプラグインで、設定項目が少なく導入しやすいのが特徴です。
機能の範囲や無料版と有料版の区分は変わることがあるため、導入時に各プラグインの公式ページで確認してください。複数のログイン制限プラグインを同時に入れると、ロックの判定が二重になって原因の切り分けが難しくなるので、プラグイン側は1つに絞るのが無難です。
プラグインで締め出されたときは、FTP やファイルマネージャーで wp-content/plugins/ 内の該当プラグインのフォルダ名を一時的に変更すると無効化できます。Wordfence の最適化を有効にしている場合は、.user.ini や .htaccess などに追記された設定が残るため、プラグインの手順に沿って解除してください。
設定後の動作確認
遮断の設定は、実際に「止まること」と「正規の利用者が止まらないこと」の両方を確認して初めて意味があります。テストは自分のサイトのステージング環境で行います。
- 許可リストに入れていない回線(スマートフォンのモバイル回線など)から、わざと間違ったパスワードで繰り返しログインし、想定した回数で 429・チャレンジ・接続拒否のいずれかになることを確認する
- 許可リストに入れた IP からは通常どおりログインできることを確認する
- Cloudflare のイベントログ、nginx のエラーログ(limit_req の記録)、
fail2ban-client statusで遮断が記録されていることを確認する - 一定時間後に遮断が解除されることを確認する
運用を始めた後も、週に一度程度はログを見て、遮断件数が急に増えていないか、正規ユーザーからの問い合わせと遮断記録が重なっていないかを確認すると、設定の効きすぎや抜けに早く気づけます。
まとめ
- 不正ログインは、CDN・Web サーバー・ファイアウォール・プラグインの順に、手前の層ほど低い負荷で止められる
- Cloudflare では wp-login.php への POST にチャレンジやレート制限をかける。Cloudflare 経由の構成では iptables での遮断は効かない
- nginx の
limit_reqはmapで POST だけを数え、location を分けるときは fastcgi の設定を写し忘れない - fail2ban は「wp-login.php への POST が 200」を失敗として数え、
fail2ban-regexで事前に確認する - どの層でも、自分の IP の許可と、締め出されたときの解除手順を先に用意しておく