2026年6月3日

2026年6月3日

wp_redirect()が効かない原因と対処|exit忘れ・headers already sent・フックの時期

wp_redirect() は、WordPressでユーザーを別のURLへ移動させる関数です。呼び出すだけで移動しそうに見えますが、実際には「Locationヘッダーを送る」だけの関数なので、呼び出す場所や後ろの処理によっては、何も起きなかったり、別のページが表示されたりします。

この記事では、wp_redirect() が効かないときによくある原因と、その直し方を説明します(WordPress 6.9 のソースコードで確認、2026年10月時点)。

wp_redirect() がしていること

wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) の処理は次のとおりです。

  • $location が空なら何もせず false を返す(wp_redirect フィルターで空にされた場合も同じ)
  • ステータスコードが300〜399の範囲外なら、wp_die() でエラー画面を表示して止まる
  • X-Redirect-By: WordPress ヘッダーと Location ヘッダーを送り、true を返す

処理を終了させる機能はありません。そのため、呼び出したあとに exit; を書かないと、PHPはそのまま続きの処理を実行し、ページの本文まで出力します。

原因1:exit を書いていない

wp_redirect() のあとに処理が続くと、続きの処理でさらにリダイレクトやデータの保存が行われたり、テンプレートが出力されたりします。呼び出したら、すぐに exit; で終了します。

<?php
wp_safe_redirect( home_url( '/thanks/' ) );
exit;

原因2:出力が始まったあとに呼んでいる(headers already sent)

HTTPのヘッダーは、本文を1文字でも出力したあとには送れません。テンプレートファイルの途中(get_header() のあとなど)で wp_redirect() を呼ぶと、ヘッダーが送られず、リダイレクトされません。WP_DEBUG を有効にしていると、次のような警告が出ます。

Warning: Cannot modify header information - headers already sent by (output started at /path/to/wp-content/themes/mytheme/header.php:12)

output started at のあとに、最初に出力したファイルと行番号が表示されます。テンプレートの途中で呼んでいる場合は、次の「正しいフックで呼ぶ」の形に直します。

ファイルの先頭や末尾にある空白・改行、文字コードの BOM(UTF-8 の先頭に付く目に見えない3バイト)も「出力」になります。functions.php やプラグインのファイルは、PHPだけのファイルなら末尾の ?> を書かないようにし、文字コードは BOM なしの UTF-8 で保存してください。

正しいフックで呼ぶ

リダイレクトは、ヘッダーを送る前に実行されるフックの中で行います。用途ごとの目安は次のとおりです。

用途 フック 理由
公開ページ(特定の固定ページ・投稿など) template_redirect 表示するページが決まったあと、テンプレートを読み込む前に実行される。is_page() などの条件分岐タグが使える
管理画面 admin_init 管理画面の出力が始まる前に実行される
ページの種類に関係なく全体 init ただし、is_page() などはまだ使えない(どのページかが決まっていない)

固定ページ「旧お問い合わせ」(スラッグ old-contact)を、新しいページへ転送する例です。

<?php
add_action( 'template_redirect', function () {
    if ( is_page( 'old-contact' ) ) {
        wp_safe_redirect( home_url( '/contact/' ), 301 );
        exit;
    }
} );

このコードは子テーマの functions.php か、自作のプラグインに書きます。

原因3:wp_safe_redirect() で外部URLに移動しない

wp_safe_redirect() は、移動先が自分のサイトと同じホスト(または許可したホスト)のときだけ移動します。それ以外のURLを渡すと、移動先が管理画面(admin_url())に置き換えられます。「外部サイトに移動させたいのに管理画面やログイン画面に飛ぶ」ときは、これが原因です。

ユーザーの入力(URLのパラメーターなど)から移動先を決める場合は、知らない外部サイトへ誘導される「オープンリダイレクト」を防ぐため、wp_safe_redirect() を使います。決まった外部サイトに移動させたいときは、allowed_redirect_hosts フィルターでそのホストを許可します。

<?php
add_filter( 'allowed_redirect_hosts', function ( $hosts ) {
    $hosts[] = 'shop.example.com';
    return $hosts;
} );

移動先がコードの中で決まっていて、ユーザーの入力を含まない場合は wp_redirect() でもかまいません。

原因4:キャッシュが古いリダイレクトを返している

  • ブラウザー:301(恒久的な移動)は、ブラウザーに記録されることがあります。一度301を返すと、コードを直したあとも同じブラウザーでは古い移動先に飛び続けることがあります。動作を確かめている間は302(初期値)を使い、確認できてから301に変えます
  • ページキャッシュ・CDN:キャッシュ系のプラグインやCDNが、リダイレクトの応答やリダイレクト前のページを保存していることがあります。コードを変えたら、キャッシュを削除してから確かめます

ブラウザーのキャッシュの影響を受けずに確かめるには、curl で応答のヘッダーを見ます。

curl -sI https://example.com/old-contact/

HTTP/2 301 などのステータスと location: の行が、意図したとおりかを確認します。x-redirect-by: の行があれば、そのリダイレクトはWordPress(またはその値を付けたプラグイン)が出しています。この行が無いリダイレクトは、.htaccess、Webサーバー、CDNなど、WordPressの外で出ている可能性があります。

原因5:リダイレクトがループする

ブラウザーに「リダイレクトが繰り返し行われました」と表示される場合は、移動先でも同じ条件が成り立ち、また同じリダイレクトが実行されています。curl でたどると、どこで繰り返しているかが分かります。

curl -sIL --max-redirs 5 https://example.com/old-contact/ | grep -i -E '^(HTTP|location)'

よくある原因は次の2つです。

  • 条件が移動先でも成り立つ:たとえば「ログインしていなければログインページへ」という条件を、ログインページ自身にも適用している。移動先のページを条件から外します
  • リバースプロキシやロードバランサーの後ろでHTTPSを判定できていない:CDNやロードバランサーがHTTPSを受けてWordPressにはHTTPで渡す構成では、is_ssl() が false になり、「HTTPSへ移動させる」処理が繰り返されます。プロキシが X-Forwarded-Proto ヘッダーを付けている場合は、wp-config.php の「編集が必要なのはここまでです」より上に次の行を書きます
<?php
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
    $_SERVER['HTTPS'] = 'on';
}

このヘッダーを、プロキシが毎回付け直す(外部から届いた同名のヘッダーを上書きする)構成でのみ使ってください。プロキシを通らずにWordPressへ直接届く経路があると、外部から同じヘッダーを偽って送れます。

自分で書いたHTTPSへのリダイレクトのコードは、.htaccess やサーバーの設定、CDNの設定と重なっていないかも確認します。同じ役割のリダイレクトは1か所にまとめます。

原因6:別のプラグインが移動先を書き換えている

wp_redirect フィルターは、移動先のURLを書き換えたり空にしたりできます。空にされると wp_redirect() は false を返し、何もしません。戻り値を確かめると、この状態かどうかが分かります。

<?php
if ( ! wp_safe_redirect( home_url( '/contact/' ) ) ) {
    error_log( 'redirect was cancelled' );
}
exit;

false が返る場合は、プラグインを1つずつ無効にして、どれがフィルターを付けているかを探します。

まとめ

  • wp_redirect() はヘッダーを送るだけ。呼んだらすぐ exit; を書く
  • 出力が始まったあとでは効かない。公開ページは template_redirect、管理画面は admin_init で呼ぶ
  • ユーザーの入力から移動先を決めるときは wp_safe_redirect() を使い、外部ホストは allowed_redirect_hosts で許可する
  • 確認は curl -sI で行い、テスト中は302を使う。ループは curl -sIL でたどる

お気軽にご相談ください

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