WordPressでリダイレクトが多すぎます(ERR_TOO_MANY_REDIRECTS)の対処法

実測 に検証

ブラウザに「リダイレクトが多すぎます」(ERR_TOO_MANY_REDIRECTS)と出る。 あるいはログインしても管理画面に入れず、ログイン画面に戻される。

ループには 2 種類あり、ログの出方が正反対です。 どちらかを先に判別すると、調べる場所が決まります。

2 種類のループ

実測した比較です。

ステータスerror.logaccess.log
外部リダイレクトのループ301 を繰り返す何も出ない301 が並ぶ
内部リライトのループ500AH00124500

エラーログに何も出ていないのにサイトが見られない場合は、 外部リダイレクトのループです。エラーとして扱われないため、ログを見ても 何も分かりません。これが調査を空振りさせます。

外部リダイレクトのループ

実測しました。.htaccess に「SSL 強制」の規則を入れた状態です。

RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ http://localhost:8082/$1 [R=301,L]

結果です。

Apache: HTTP 301 リダイレクト回数=8   ← curl の上限で打ち切り
Location: http://localhost:8082/       ← 自分自身
error.log の行数: 0
AH00124 の件数: 0

Location自分自身を指しています。ブラウザは 301 を追いかけ続け、 上限に達して ERR_TOO_MANY_REDIRECTS を表示します。

このページは動作していません。リダイレクトが繰り返し行われました。ERR_TOO_MANY_REDIRECTS

同じ状態を Chrome で開いた画面。サーバーのエラーログにはこのとき何も出ていない

なお nginx 側は .htaccess を読まないため 200 のまま正常でした。 2 台構成やステージングで「片方だけループする」のはこの非対称性です。

なぜ条件が満たされないのか

%{HTTPS} は、Apache 自身が SSL を終端している場合にだけ on になります。

ロードバランサや CDN、リバースプロキシが SSL を終端して、 そこから先は HTTP でサーバーに渡す構成では、 Apache から見ると常に off です。

  • ブラウザ → (https)→ プロキシ → (http)→ Apache
  • Apache は「HTTPS off」と判断して https にリダイレクト
  • ブラウザは https で再度アクセスするが、Apache には再び http で届く
  • 永久に繰り返す

正しくは、プロキシが付けるヘッダを見ます。

RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

WordPress 側も同じ対応が必要です。

// wp-config.php の、wp-settings.php を読み込む行より前に置く
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
	$_SERVER['HTTPS'] = 'on';
}

これが無いと、WordPress は「自分は http で動いている」と思い込み、 https のページから http の URL を出力します(混在コンテンツの原因にもなります)。

SSL 強制プラグインを 2 つ入れた場合

「Really Simple SSL」のような SSL 強制プラグインと、.htaccess の規則、 サーバー側の設定が同時に有効だと、互いにリダイレクトし合います。

SSL 強制は 1 箇所だけにします。どこでやっているか分からない状態が いちばん危険です。

内部リライトのループ

こちらは 500 になり、ログに残ります。

AH00124: Request exceeded the limit of 10 internal redirects due to probable
configuration error.

外部へのリダイレクトではなく、Apache 内部で書き換えを繰り返している状態です。 RewriteBase の誤りが典型です。

→ 詳細は .htaccess で 500 エラー

トップページだけ表示されて個別記事が 500という特徴的な症状になります。

ログイン画面との往復

「リダイレクトが多すぎます」とは出ないのに、 ログインしても毎回ログイン画面に戻される場合です。

実測しました。WP_HOMEWP_SITEURL を食い違わせた状態です。

WP_HOME    = http://localhost:8080
WP_SITEURL = http://127.0.0.1:8080

管理画面にアクセスしたときの挙動です。

HTTP/1.1 302 Found
Location: http://127.0.0.1:8080/wp-login.php?redirect_to=http%3A%2F%2Flocalhost%3A8080%2Fwp-admin%2F

別ホストのログイン画面に飛ばされています。 ここが問題になるのは Cookie の仕組みです。

WordPress が発行するログイン Cookie には Domain 属性が付きません (実測: Set-Cookie: wordpress_test_cookie=...; path=/; HttpOnly)。 Cookie はアクセスしたホストにだけ保存されます。

  1. 127.0.0.1 のログイン画面でログイン → Cookie は 127.0.0.1 に保存される
  2. redirect_to が指す localhost/wp-admin/ に飛ばされる
  3. localhost には Cookie が無いので未ログイン扱い
  4. ログイン画面に戻される(1 に戻る)

HTTP レベルの無限ループではないので ERR_TOO_MANY_REDIRECTS は出ません。 利用者から見ると「ログインできない」という報告になります。 → ログインできない時の症状別の見分け方

同じことが www 有り無し、http / https、独自ドメインと サーバー既定ドメインの混在でも起きます。ホストが 1 文字でも違えば別扱いです。

切り分けの順番

# リダイレクトの連鎖を数える
curl -s -o /dev/null -L --max-redirs 10 \
  -w 'status=%{http_code} redirects=%{num_redirects} final=%{url_effective}\n' \
  https://example.com/

# 1 段目だけ見る
curl -s -o /dev/null -D - https://example.com/ | grep -iE '^HTTP/|^location'
観測原因
301 が延々と続き、Location が自分自身外部リダイレクトのループ(SSL 強制の条件ミス、プラグインの重複)
500 + error.logAH00124内部リライトのループ(RewriteBase など)
Location のホストが違うWP_HOME / WP_SITEURL の不一致。Cookie が別ホストに置かれる
httphttps を往復しているプロキシ配下で X-Forwarded-Proto を見ていない

error.log を見て何も無ければ、外部リダイレクトのループと判断して .htaccess とプラグインの設定に移ります。

復旧できないときの入口

ループしていると管理画面に入れないので、プラグインを止められません。

  • .htaccess を退避する(リネームする)→ サーバー側の規則が止まる
  • FTP でプラグインのフォルダをリネームする → SSL 強制プラグインが止まる

WP-CLI が無い環境での復旧

.htaccess をリネームするとパーマリンクが 404 になりますが、 トップページと管理画面には入れるようになります。 まず入れる状態を作ってから原因を直します。

再現手順

cp src/.htaccess /tmp/ht.bak

# 自分自身に 301 する規則を、WordPress ブロックより前に入れる
# RewriteCond %{HTTPS} off
# RewriteRule ^(.*)$ http://localhost:8082/$1 [R=301,L]

curl -s -o /dev/null -L --max-redirs 8 -w '%{num_redirects} 回\n' http://localhost:8082/
curl -s -o /dev/null -D - http://localhost:8082/ | grep -i location
wc -l < logs/apache/error.log      # 0 行(エラーとして残らない)

cp /tmp/ht.bak src/.htaccess