2026年5月20日
2026年5月20日
WordPressのログインページを保護する方法(Basic認証・IP制限・試行回数制限・2段階認証)
WordPress のログイン画面は、どのサイトでも /wp-login.php にあります。そのため、ユーザー名とパスワードを大量に試す総当たり攻撃(ブルートフォース攻撃)を受けやすく、XML-RPC(/xmlrpc.php)も同じ目的で狙われます。
対策は1つに頼らず、次のように組み合わせます。
| 対策 | 効果 | 向いている場面 |
|---|---|---|
| 強いパスワード・推測しにくいユーザー名 | 総当たりで当たりにくくする | すべてのサイト |
| 2段階認証 | パスワードが漏れてもログインされにくくする | すべてのサイト、特に管理者 |
| ログイン試行回数の制限 | 同じ相手からの連続した試行を止める | すべてのサイト |
| Basic 認証 | WordPress の前にもう1つ認証を置き、PHP が動く前に止める | 管理する人が少ないサイト |
| IP アドレス制限 | 決まった場所からしかログインできなくする | 接続元の IP が固定されている場合 |
| ログイン URL の変更 | 機械的なアクセスを減らす(防御の代わりにはならない) | 補助として |
この記事の設定は Apache 2.4 と Nginx の書き方です。Apache 2.2 の Order/Allow from の書き方は使いません。作業の前に、FTP や SSH、サーバーの管理画面など、ログインできなくなったときに設定を戻す手段を確保してください。
ステップ1:ログイン画面に Basic 認証をかける
Basic 認証は、ブラウザーが出すユーザー名とパスワードの入力欄で、WordPress のログイン画面の手前にもう1つ関門を置く方法です。認証に通らないリクエストは PHP まで届かないので、サーバーの負荷も下げられます。Basic 認証の通信は暗号化されないので、サイトが HTTPS になっていることが前提です。
パスワードファイルを作る
パスワードファイルは、公開ディレクトリ(ドキュメントルート)の外に置きます。htpasswd は Ubuntu/Debian では apache2-utils、RHEL 系では httpd-tools に入っています。
# 新しく作る(-c は既存のファイルを上書きするので、2人目以降は付けない)
sudo htpasswd -c -B /etc/apache2/.htpasswd admin_basic
# 2人目を追加する
sudo htpasswd -B /etc/apache2/.htpasswd another_user
-B はパスワードを bcrypt で保存するオプションです。詳しくは htpasswdの作り方 を参照してください。
Apache 2.4:wp-login.php にかける
WordPress をインストールしたディレクトリの .htaccess(wp-login.php と同じ場所)に追記します。.htaccess で認証を設定するには、VirtualHost 側で AllowOverride AuthConfig(または All)が許可されている必要があります。
<Files "wp-login.php">
AuthType Basic
AuthName "Login"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
</Files>
WordPress が # BEGIN WordPress と # END WordPress の間に書く部分は、パーマリンク設定の保存などで書き換えられることがあるので、その外に書きます。
Apache 2.4:wp-admin にかける
.htaccess の中では <Directory> は使えません。wp-admin ディレクトリの中に別の .htaccess を作ります。
# wp-admin/.htaccess
AuthType Basic
AuthName "Admin"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
# 公開側の機能が使う admin-ajax.php は認証の外に出す
<Files "admin-ajax.php">
Require all granted
</Files>
wp-admin/admin-ajax.php は、ログインしていない訪問者向けの機能(フォームや「いいね」ボタンなど)にも使われます。認証をかけたままにすると、それらが動かなくなります。
Basic 認証の注意点
- パスワード保護の投稿・固定ページ: 訪問者がパスワードを送ると
wp-login.php?action=postpassに送信されます。wp-login.phpに Basic 認証をかけると、訪問者にも Basic 認証の入力欄が出てしまいます。パスワード保護のページを使っているサイトでは、IP 制限か試行回数の制限を選んでください。 - 会員登録やパスワードの再設定: これらも
wp-login.phpを使うので、利用者が登録するサイトには向きません。
Nginx での設定は NginxのBasic認証の設定 を参照してください。
ステップ2:IP アドレスでログインを制限する
自宅やオフィスの IP アドレスが固定されているなら、その IP からしか wp-login.php を開けないようにできます。携帯回線や IP が変わる回線から作業する人がいる場合は、ほかの方法を選びます。
Apache 2.4
.htaccess に書きます。IP アドレスとネットワーク(CIDR)を空白で区切って並べられます。コメントは行の途中ではなく、独立した行に書きます(Apache は行末のコメントを受け付けません)。
# 203.0.113.10 はオフィス、198.51.100.0/24 は VPN の例
<Files "wp-login.php">
Require ip 203.0.113.10 198.51.100.0/24
</Files>
wp-admin も制限する場合は、ステップ1と同じように wp-admin/.htaccess に Require ip を書き、admin-ajax.php は Require all granted で外します。
Nginx
Nginx では location = /wp-login.php を作り、その中にも PHP を処理する設定を書きます。allow/deny だけを書くと、許可した IP でもファイルがダウンロードされたり、PHP として実行されなかったりします。
location = /wp-login.php {
allow 203.0.113.10;
allow 198.51.100.0/24;
deny all;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
fastcgi_pass のソケットは環境によって違うので、既存の location ~ \.php$ と同じものにしてください。変更後は sudo nginx -t で確認してから sudo systemctl reload nginx で反映します。
プロキシや CDN の後ろにある場合
Cloudflare などの CDN やロードバランサーを経由していると、サーバーから見える接続元は CDN の IP になり、IP 制限が意図どおりに働きません。先に Web サーバーで訪問者の IP を復元しておきます(CloudflareのCDN設定 のステップ3)。
詳しくは ログインページのIP制限 も参照してください。
ステップ3:ログイン試行回数を制限する
WordPress 本体には、ログインの失敗回数を数えて止める機能はありません。プラグイン(Limit Login Attempts Reloaded など)を使うか、次のようなコードを独自プラグインかテーマの functions.php に書きます。
このコードは、同じ IP アドレスから30分以内に5回失敗すると、その IP からのログインを30分止めます。ユーザー名ごとではなく IP ごとに数えるので、ユーザー名を変えながら試す攻撃にも効きます。XML-RPC やアプリケーションパスワードの認証も同じ authenticate フィルターを通るので、同じく数えられます。
<?php
/**
* IP アドレスごとにログイン失敗回数を数え、上限を超えたら一定時間ログインを止める。
*/
const MYSITE_LOGIN_MAX_FAILS = 5;
const MYSITE_LOGIN_LOCK_SEC = 30 * MINUTE_IN_SECONDS;
function mysite_login_fail_key(): string {
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
return 'mysite_login_fail_' . md5( $ip );
}
// 認証の処理(優先度20)のあとで、上限に達していればエラーに差し替える。
add_filter( 'authenticate', function ( $user, $username, $password ) {
if ( '' === (string) $username ) {
return $user;
}
$fails = (int) get_transient( mysite_login_fail_key() );
if ( $fails >= MYSITE_LOGIN_MAX_FAILS ) {
return new WP_Error(
'too_many_retries',
sprintf(
'ログインの失敗が続いたため、一時的にログインを止めています。%d分ほどたってから試してください。',
MYSITE_LOGIN_LOCK_SEC / MINUTE_IN_SECONDS
)
);
}
return $user;
}, 30, 3 );
// 失敗したら回数を増やす。
add_action( 'wp_login_failed', function ( string $username ): void {
$key = mysite_login_fail_key();
$fails = (int) get_transient( $key );
set_transient( $key, $fails + 1, MYSITE_LOGIN_LOCK_SEC );
} );
// 成功したら回数を消す。
add_action( 'wp_login', function ( string $user_login ): void {
delete_transient( mysite_login_fail_key() );
} );
- 止められている間に試すと、それも失敗として数えられ、止める時間がそこから30分延びます。
REMOTE_ADDRを使うので、CDN やプロキシの後ろでは訪問者の IP を復元しておく必要があります。復元していないと、全員が同じ IP として数えられ、攻撃1件でほかの人もログインできなくなります。- 自分がロックされたときは、30分待つか、WP-CLI で
wp transient delete --allを実行して解除します(ほかのトランジェントも消えますが、必要なものは再生成されます)。
ほかの書き方は プラグインを使わないログイン試行回数の制限 と ログイン試行回数の制限 を参照してください。
ステップ4:2段階認証を有効にする
2段階認証にすると、パスワードに加えて、スマートフォンの認証アプリが表示する確認コードなどが必要になります。パスワードが漏れても、それだけではログインされません。
WordPress のコントリビューターが開発している「Two-Factor」プラグイン(2026年11月時点で 0.17.0、WordPress 7.0 以上・PHP 7.4 以上)の場合:
- 「プラグイン」→「新規プラグインを追加」で Two-Factor をインストールして有効化します。
- 「ユーザー」→「プロフィール」の「Two-Factor Options」で、使う方法を選びます。認証アプリ(TOTP)を使う場合は、表示された QR コードを Google Authenticator や 1Password などのアプリで読み取り、表示されたコードを入力して確定します。
- スマートフォンを失くしたときのために、バックアップコードも作って安全な場所に保管します。
このほかに確認コードのメール送信も選べます。設定は利用者がそれぞれのプロフィールで行います(管理者はほかの利用者のプロフィールも編集できます)。全員や管理者だけに2段階認証を強制したい場合は、WP 2FA など強制の機能を持つプラグインを使います(2段階認証を強制する方法)。
設定の詳しい手順は WordPressの2段階認証の設定 を参照してください。
ステップ5:ログイン URL を変える(補助として)
ログイン URL を変えると、/wp-login.php を機械的に狙うアクセスが減ります。ただし URL が知られれば効果はないので、ここまでの対策の代わりにはなりません。
自作のコードで wp-login.php を隠すと、ログアウトやパスワードの再設定、パスワード保護ページなどの動作を壊しやすいので、プラグインを使います。WPS Hide Login の場合は、有効化したあと「設定」→「WPS Hide Login」で新しいログイン用のスラッグを決めます。ページキャッシュのプラグインを使っている場合は、新しいログイン URL をキャッシュの除外に加えてください(WP Rocket は対応済みと公式に書かれています)。
ステップ6:XML-RPC を使っていなければ止める
xmlrpc.php は、外部のアプリやサービスから投稿するための古い仕組みで、ログイン画面と同じようにパスワードの総当たりに使われます。Jetpack や一部のスマートフォンアプリなど、XML-RPC を使うものが無ければ止められます。止め方は XML-RPCを無効化する方法 を参照してください。
攻撃を受けているかをログで確かめる
WordPress はログインに失敗してもステータスコード200でログイン画面を表示し直し、成功すると302でリダイレクトします。そのため、403 や 429 を数えても失敗は分かりません。wp-login.php と xmlrpc.php への POST を接続元ごとに数えます。
# wp-login.php への POST が多い接続元 IP(Nginx の標準のログ形式)
grep 'POST /wp-login.php' /var/log/nginx/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
# xmlrpc.php への POST
grep -c 'POST /xmlrpc.php' /var/log/nginx/access.log
Apache の場合はログのパスを /var/log/apache2/access.log(RHEL 系は /var/log/httpd/access_log)に変えます。同じ IP からの大量の POST が続いている場合は、ここまでの対策に加えて、ファイアウォールや fail2ban での遮断も検討します。
注意点
- IP 制限を入れる前に、自分の IP が許可リストに入っているかを確かめます。間違えた場合は、FTP や SSH で
.htaccessを直します。 - ログイン URL の変更や試行回数の制限は、サーバー全体の負荷を下げる効果は限られます。攻撃が多いときは、Basic 認証や IP 制限のように PHP が動く前に止める方法が向いています。
- 管理者のユーザー名に
adminなど推測しやすい名前を使っている場合は、新しい管理者ユーザーを作ってから古いユーザーを削除します(投稿は新しいユーザーに引き継ぎます)。
まとめ
- 2段階認証と試行回数の制限は、どのサイトでも入れておきたい対策です。
- 管理する人が少なければ Basic 認証、接続元が固定なら IP 制限を加え、PHP が動く前に止めます。設定は Apache 2.4 の
Requireか Nginx のallow/denyで書きます。 wp-adminに認証をかけるときはadmin-ajax.phpを外し、パスワード保護ページや会員登録への影響を確かめます。- 攻撃の有無は、
wp-login.phpとxmlrpc.phpへの POST の数で確かめます。