2026年5月20日
2026年5月20日
WordPressのDDoS攻撃対策|Cloudflare・Nginxのレート制限・Fail2banの設定
DDoS(分散型サービス妨害)攻撃は、多数の送信元から大量のリクエストを送りつけ、サーバーを応答できない状態にする攻撃です。WordPress のサイトでは、ログイン画面の wp-login.php や xmlrpc.php に自動化されたアクセスが集中し、PHP とデータベースの処理が追いつかなくなる例がよく見られます。
対策は、サーバーに届く前に止める(Cloudflare などの CDN・WAF)、届いたリクエストを Web サーバーで絞る(Nginx・Apache)、繰り返し来る送信元をまとめて止める(Fail2ban)の3段で考えます。回線を埋めるほどの大規模な攻撃は、自分のサーバーの設定だけでは防げません。その場合は Cloudflare のような前段のサービスや、ホスティング会社の対策が必要です。
この記事の Cloudflare の内容は、Cloudflare の公式ドキュメントで確認したものです(2026年11月時点)。
まず状況を確かめる
サイトが重い・落ちるときは、本当に攻撃なのか、どこが狙われているのかを先に調べます。ログのパスは環境に合わせて読み替えてください(Ubuntu の Apache は /var/log/apache2/access.log、Nginx は /var/log/nginx/access.log)。
# アクセスの多い送信元IPの上位20件
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# アクセスの多いURLの上位20件(クエリ文字列は除く)
awk '{print $7}' /var/log/nginx/access.log | cut -d'?' -f1 | sort | uniq -c | sort -rn | head -20
# wp-login.php と xmlrpc.php へのアクセス元の上位20件
grep -E '"(GET|POST) /(wp-login|xmlrpc)\.php' /var/log/nginx/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
# 接続数の概要と、443番ポートで確立している接続の数
ss -s
ss -tn state established '( sport = :443 )' | wc -l
見方の目安は次のとおりです。
- 少数の IP から大量に来ている: 後で説明する IP 単位の制限や Fail2ban が効きます。
- 多数の IP から少しずつ来ている: IP 単位の制限は効きにくく、Cloudflare などの前段での対策が中心になります。
wp-login.phpやxmlrpc.phpに集中している: そのパスだけを強く制限します。
Cloudflare を使っている場合、サーバーのログに残る IP は Cloudflare のものになります。後の「Cloudflare を使うときに訪問者の IP をログに残す」の設定をしてから調べます。
Cloudflare で前段で止める
Cloudflare にドメインを登録し、ネームサーバーを Cloudflare に向けて、DNS レコードをプロキシ(オレンジの雲)にすると、通信は Cloudflare を経由してサーバーに届きます。導入の手順は「WordPress で Cloudflare の DDoS 保護を使う」を参照してください。
HTTP DDoS 攻撃の保護
Cloudflare の HTTP DDoS attack protection(HTTP の DDoS 攻撃を検出して止める管理ルール)は、無料プランを含むすべてのプランで常に有効です。設定を変えなくても働きます。
感度や動作は、ダッシュボードの「Security rules」の「DDoS protection」タブで「Create override」から変えられます。無料プランで作れる上書き(override)は1つで、対象はすべてのリクエストです。正常な訪問者まで止まる、逆に攻撃が通るといった問題が無い限り、既定のまま使ってかまいません。
Under Attack モード(攻撃を受けている間だけ)
I'm Under Attack モードを有効にすると、訪問者に確認のページが表示され、ブラウザでの確認(数秒)を通った訪問者だけがサイトに進めます。ゾーン全体で有効にするには、ゾーンの概要ページの「Quick Actions」で「Under Attack Mode」をオンにします。
Cloudflare 自身が「最後の手段」と位置づけている機能で、すべての訪問者に確認ページが出るほか、JavaScript を使えないクライアントや外部の解析ツールに影響します。攻撃が収まったらオフに戻します。管理画面など特定のパスだけで有効にしたい場合は、Configuration Rules で対象のパスを指定して「I'm Under Attack」をオンにする方法もあります。
レート制限ルール(無料プランは1つ)
レート制限ルールは、同じ IP から一定時間内に決まった回数を超えるリクエストが来たら、しばらく止める機能です。無料プランの条件は次のとおりです。
| 項目 | 無料プランの条件 |
|---|---|
| ルールの数 | 1つ |
| 条件に使える項目 | パス(URI Path)と Verified Bot |
| 数える単位 | IP |
| 数える期間 | 10秒 |
| 止める時間 | 10秒 |
無料プランでは1つしか作れないため、狙われやすい /wp-login.php を対象にするのが現実的です。数える期間が10秒なので、「10秒に5回を超えたら止める」程度から始め、ログイン画面を使う人が困らないか確かめて調整します。設定の例は「Cloudflare で wp-login.php へのアクセスを制限する」で説明しています。
WAF の管理ルール
無料プランで使える WAF の管理ルールは「Cloudflare Free Managed Ruleset」です。広く悪用されている重大な脆弱性への攻撃を止めるルールで、すべてのプランで使えます。WordPress 向けのルールを含む「Cloudflare Managed Ruleset」は Pro プラン以上です。WAF の設定は「Cloudflare WAF の設定」を参照してください。
Cloudflare を使うときに訪問者の IP をログに残す
Cloudflare を経由すると、サーバーから見た接続元は Cloudflare の IP になります。訪問者の IP は CF-Connecting-IP ヘッダーに入っています。
ただし、このヘッダーは誰でも付けて送れます。PHP で $_SERVER['HTTP_CF_CONNECTING_IP'] を無条件に REMOTE_ADDR に入れると、Cloudflare を通さずに直接サーバーへ来た攻撃者が、好きな IP になりすませます。Cloudflare の IP から来た接続のときだけヘッダーを信用するよう、Web サーバー側で設定します。
Nginx では ngx_http_realip_module を使います。
# http {} ブロック、または server {} ブロックに書く
# Cloudflare の IP の範囲を1つずつ書く(下の2行は書き方の例)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 2400:cb00::/32;
# ……公開されている範囲をすべて書く
real_ip_header CF-Connecting-IP;
Apache 2.4 では mod_remoteip を使います(Ubuntu・Debian の例)。
sudo a2enmod remoteip
/etc/apache2/conf-available/remoteip.conf を作って次のように書きます。
RemoteIPHeader CF-Connecting-IP
# Cloudflare の IP の範囲を1つずつ書く(下の2行は書き方の例)
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 2400:cb00::/32
sudo a2enconf remoteip
sudo apache2ctl configtest
sudo systemctl restart apache2
Apache はログの書式の %h を %a に変えると、ログに訪問者の IP が残ります。Cloudflare の IP の範囲は https://www.cloudflare.com/ips/ で公開されており、変わることがあるため、記事やコピーした一覧ではなく、このページの最新の一覧を使ってください。
Nginx でリクエストを絞る
Nginx の limit_req で、IP ごとのリクエストの頻度を制限します。limit_req_zone は http {} ブロックに書きます(Ubuntu の場合は /etc/nginx/nginx.conf の http {} の中、または /etc/nginx/conf.d/ のファイル)。
# http {} ブロックに書く
# IP ごとに、ログイン画面は1分に5回まで
limit_req_zone $binary_remote_addr zone=wplogin:10m rate=5r/m;
サイトの server {} ブロックに、wp-login.php と xmlrpc.php の設定を書きます。PHP-FPM のソケットのパスは環境に合わせます。
server {
# ……既存の設定……
# wp-login.php: IP ごとに1分5回まで(一時的に2回まで超過を許す)
location = /wp-login.php {
limit_req zone=wplogin burst=2 nodelay;
limit_req_status 429;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# xmlrpc.php: 使っていなければ拒否する
location = /xmlrpc.php {
deny all;
access_log off;
}
}
location = /wp-login.phpは、通常のlocation ~ \.php$より優先されます。この中に PHP を処理する設定も書かないと、wp-login.phpが動かなくなります。burstは一時的に超えてよい回数、nodelayは超えた分を待たせずにすぐ処理する指定です。- 制限を超えたリクエストには 429(Too Many Requests)を返します。
xmlrpc.php は Jetpack や一部のスマートフォン用アプリが使います。使っている場合は拒否せず、wp-login.php と同じように頻度の制限にします。詳しくは「XML-RPC を無効にするセキュリティ対策」を参照してください。
sudo nginx -t
sudo systemctl reload nginx
サイト全体(location /)に頻度の制限をかける方法もありますが、1ページの表示で画像・CSS・JavaScript を含む多数のリクエストが出るため、値が小さいと普通の訪問者が止まります。かける場合はアクセスログで普段の頻度を調べてから値を決めます。limit_req の詳しい使い方は「Nginx の limit_req でレート制限をする」で説明しています。
Apache でログイン画面を守る
Apache には limit_req にあたる標準の機能がありません。管理者の IP が決まっている場合は、wp-login.php と xmlrpc.php を許可した IP 以外から開けないようにするのが簡単です。WordPress を置いたディレクトリの .htaccess に書きます(Apache 2.4 の書き方)。
<FilesMatch "^(wp-login\.php|xmlrpc\.php)$">
Require ip 203.0.113.10
Require ip 198.51.100.0/24
</FilesMatch>
Order Deny,Allow と Allow from は Apache 2.2 の書き方です。2.4 では Require を使います。IP は自分の環境のものに置き換えます。
自宅や外出先など、管理者の IP が変わる場合はこの方法は使えません。Cloudflare のレート制限や、次の Fail2ban を使います。Apache で頻度による制限をかける mod_evasive は「mod_evasive で Apache を DDoS から守る」で説明しています。
Fail2ban で繰り返し来る IP を止める
Fail2ban は、ログを監視して、決まったパターンのアクセスを繰り返す IP をファイアウォールで一定時間止めるソフトです。Ubuntu・Debian では次のように入れます。
sudo apt install -y fail2ban
/etc/fail2ban/filter.d/wordpress-login.conf を作り、ログイン画面と XML-RPC への POST を数えるフィルターを書きます。
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php
^<HOST> .* "POST /xmlrpc\.php
ignoreregex =
/etc/fail2ban/jail.local に、このフィルターを使う設定を追加します。
[wordpress-login]
enabled = true
filter = wordpress-login
backend = auto
logpath = /var/log/nginx/access.log
port = http,https
maxretry = 10
findtime = 60
bantime = 3600
60秒(findtime)の間に10回(maxretry)POST した IP を、3600秒(bantime)止めます。正しいログインも POST なので、maxretry は普段のログインでは届かない値にします。
フィルターがログに当たるかを確かめてから有効にします。
# フィルターがログの何行に当たるか確かめる
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress-login.conf
sudo systemctl restart fail2ban
# 止めている IP を確かめる
sudo fail2ban-client status wordpress-login
# 誤って止めた IP を解除する
sudo fail2ban-client set wordpress-login unbanip 203.0.113.10
Cloudflare を経由している場合は注意が必要です。サーバーに接続してくるのは Cloudflare の IP なので、ログに訪問者の IP を残していても、サーバーのファイアウォールで訪問者の IP を止めても効果がありません。逆に、ログに Cloudflare の IP が残っている状態で動かすと、Cloudflare の IP を止めてしまい、普通の訪問者まで入れなくなります。Cloudflare を使う場合は、Cloudflare 側のレート制限や WAF で止めるのが基本です。
WordPress 用の Fail2ban の設定は「Fail2ban で WordPress へのブルートフォース攻撃を防ぐ」でも説明しています。
WordPress の負荷を下げておく
攻撃の対象が普通のページの場合、1回ごとに PHP とデータベースが動くと、少ないリクエストでもサーバーが重くなります。ページキャッシュ(キャッシュ用のプラグインや、Nginx の FastCGI キャッシュ、Cloudflare のキャッシュ)で、PHP を通さずに返せるページを増やしておくと、同じ量のアクセスでも耐えやすくなります。
PHP の中(functions.php など)で User-Agent を見て 403 を返す方法は、WordPress を読み込んだ後に判定するため、負荷を下げる効果は小さく、User-Agent は簡単に偽れます。止めるなら Web サーバーや Cloudflare など、PHP より前で止めます。
注意事項
- 設定を変える前に、設定ファイルを控えておき、
sudo nginx -tやsudo apache2ctl configtestで確かめてから反映します。 - 制限の値を小さくしすぎると、普通の訪問者や管理者が止まります。まずはアクセスログで普段の頻度を調べ、余裕のある値から始めます。
- 自分の IP が止まったときに備えて、サーバーのコンソール(ホスティング会社の管理画面など)から入れる方法を確かめておきます。
まとめ
- アクセスログで、攻撃元の IP の数と狙われている URL を調べる
- Cloudflare の HTTP DDoS 保護は無料プランでも常に有効。攻撃中だけ Under Attack モードを使い、無料プランのレート制限1つは
/wp-login.phpに使う - Cloudflare を経由するときは、Cloudflare の IP から来た接続のときだけ
CF-Connecting-IPを信用するよう Web サーバーで設定する - Nginx は
limit_reqでログイン画面を制限し、使っていないxmlrpc.phpは拒否する。Apache 2.4 はRequire ipを使う - Fail2ban は Cloudflare を経由しないサーバーで使い、
fail2ban-regexでフィルターを確かめてから有効にする