検証環境
client <---> nginx(http:80) <---> apache(http:8080)
症状:Wordpress設置後ページが正常に表示されない
ディレクトリ構成は/var/www/html/wp
テーマ依存ではなく、Wordpress設置後のみに起こる症状でした。
Chromeさん
このページは動作していません
localhost でリダイレクトが繰り返し行われました。
Cookie を消去してみてください.
ERR_TOO_MANY_REDIRECTS
--
JS error http://localhost:8000/ net::ERR_TOO_MANY_REDIRECTS
原因:繰り返しリダイレクトが行われている
↑ブラウザのエラーからhtaccess周りの設定かなと思いましたが、nginxだしなーとググれば答えが見つかった。
do_action('template_redirect') をコメントアウト
WordPressで無限リダイレクトが発生したときの対策と調査メモ
「wp-includes/template-loader.php」の12行目にある「do_action('template_redirect')」をコメントアウト(行頭に「//」を追加)しました。
- do_action( 'template_redirect' );
+ //do_action( 'template_redirect' );
↑のコメントアウトで確かにリダイレクトはなくなり、めでたく正常にWordpressが表示されました!
wp-includes/query.php
の4986行目 wp_old_slug_redirect
が原因だったようです。
が、
Milestone changed from Awaiting Review to 4.4.1 (引用元)
4.4.1で既に解決済みらしい。(Wordpressの使用バージョンは4.8.1)
remove_action に redirect_canonical を追加
さらにググると redirect_canonical
が原因でループが起こってるようでした。
テーマ内のfunctions.phpに下記コード追加だけでループは止まり正常に表示されます!
remove_action('template_redirect', 'redirect_canonical');
ですが根本の原因がサッパリわかりません!
っというかこのアクション消して本当に大丈夫なのかという不安感たっぷりです。
リダイレクトが起こっている本当の原因
リバースプロキシを使っているとWORDPRESSで記事の参照がループしてしまう問題について
パーマリンクを元に作成された、記事自体のパーマリンク。リダイレクトしたいURLを redirect_url と呼ぶ。
リダイレクトされる前の、実際に今アクセスされているURLを requested_url とする。
この二つが一致しないので延々とredurect_urlにリダイレクトさせようとしてしまっている。
473行目で $redirect_url
と $requested_url
が一致しない場合 return を返してます。
function redirect_canonical( $requested_url = null, $do_redirect = true ) {
略
if ( ! $redirect_url || $redirect_url == $requested_url ) {
return;
}
これを回避すれば良いようなので、それぞれvar_dumpしてみると、
string(22) "http://localhost:8000/" //$redirect_url
string(17) "http://localhost/" // $requested_url
_人人人人人人人人人_
> ポート番号!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y ̄
じつはこのrequested_urlは$_SERVERを元に作成される。サーバ環境変数。つまり見せかけ上のURL(ドメイン名)と実際に>PHPが動いているドメイン名が違っていると無限ループする、というわけだ。
この見せかけ上のドメインとPHPが動いているサーバのドメイン名が違う、というのはリバースプロキシを使っていると起こってしまう。
つまり、
379行目で $user_home
は home_url()
から$redirect_url
にURLを代入してますが、
function redirect_canonical( $requested_url = null, $do_redirect = true ) {
略
$user_home = @parse_url(home_url());
if ( !empty($user_home['host']) )
$redirect['host'] = $user_home['host'];
if ( empty($user_home['path']) )
$user_home['path'] = '/';
--
var_dump($user_home);
↓
array(4) {
["scheme"]=> string(4) "http"
["host"]=> string(9) "localhost"
["port"]=> int(9010)
["path"]=> string(1)"/"
}
62行目で $requested_url
は $_SERVER['HTTP_HOST']
と $_SERVER['REQUEST_URI']
でURLを形成していました。
function redirect_canonical( $requested_url = null, $do_redirect = true ) {
略
if ( ! $requested_url && isset( $_SERVER['HTTP_HOST'] ) ) {
// build the URL in the address bar
$requested_url = is_ssl() ? 'https://' : 'http://';
$requested_url .= $_SERVER['HTTP_HOST'];
$requested_url .= $_SERVER['REQUEST_URI'];
}
この見せかけ上のドメインとPHPが動いているサーバのドメイン名が違う、というのはリバースプロキシを使っていると起こってしまう。
解決策:redirect_url を書き換える
// nginx と Apache で リバースプロキシ した際に カノニカルURL が不一致するバグ対策
function change_ridirect_url($redirect_url) {
$redirect_url = is_ssl() ? 'https://' : 'http://';
$redirect_url .= $_SERVER['HTTP_HOST'];
$redirect_url .= $_SERVER['REQUEST_URI'];
return $redirect_url;
}
add_action('redirect_canonical', 'change_redirect_url', 10);
nginx側で回避できるのかと思いますが、
フロントのコーディングばかりでサーバーの知識が乏しいので、
今回はこんな感じで、テーマ内の functions.php
に追記してリダイレクト回避しました。
追記
Twentysevenで表示確認行っていたので気付かなかったのですが、実際に上記コードでオリジナルテーマを開発しようとすると、
home_url()
が返す値も $_SERVER['HTTP_HOST']
を返していました。(site_url()
も
ポート番号まで $_SERVER['HTTP_HOST']
で返してもらわないと、今までの資産(自作の関数群・プラグインでhome_url()
を使ってる)が使えないので、
コメントでも頂きましたがnginx側でホスト名+ポート番号
を返してもらう必要がありました。
home_url() の中身は wp-includes/link-template.php
の 2968行目に書いてます。
解決策
すごく単純
- proxy_set_header Host $host;
+ proxy_set_header Host $http_host;
nginx側の設定で$host
としているとリバースプロキシではポート番号まで返さないので $http_host
としてあげると、
$_SERVER['HTTP_HOST']
でホスト名+ポート番号までを送ってくれます。
また、
Host
ではなく、プロキシサーバのホスト名 X-Forwarded-Server
proxy_set_header X-Forwarded-Server $http_host;
として、
$_SERVER['HTTP_X_FORWARDED_FOR']
でホスト名+ポート番号を取得して、
wp-config.php
で home_url
と site_url
を上書きするーというやり方が本来の方法の様だったが、
httpsや複雑な処理を実装する予定も今回なく、 proxy_set_header Host
に直接 $http_host
に設定しました。
上記方法でホスト名+ポート番号を取得できれば、上記 解決策redirect_url-を書き換える は当然不要になりました。