wordpress公式ロゴ

wordpressで謎の転送処理されているのを突き止める

状況

この記事は「wordpressで複数ページあるとき「/page/番号」になるパーマリンクを変更する」の見出し「パーマリンク設定を追加する」から続きます。

当サイトの一覧ページのURL構造を変えようとしていました。

add_rewrite_rule() あたりを使って、独自のルールを設定してブラウザでアクセスしても「ページが見つかりません」と。
何をどう確認しても合っているはずなんです。
なのに「ページが見つかりません」と。

で、気づきました。

アドレスバーに「/blog-2.html」というURLが入れていたはずなのに知らぬ間に「/blog-2.html/page/2」という違うURLになってたんです。
「/blog-2.html/page/2」のURLで何度も確認していたので「ページが見つかりません」になるはずです。

そこでアドレスバーに「/blog-2.html」を入れて確認してみました。
あれ?
アドレスバーが「/blog-2.html/page/2」に変わっちゃいました。

「/blog-2.html」にアクセスすると何故か「/blog-2.html/page/2」にリダイレクトされているっぽいです。

調査

そこで何故リダイレクトされているか調査してみます。

※ここに記す作業は可能なら開発環境で行いましょう。本番と同じものをローカル環境に構築しておくとこういうとき安心して壊せます。

まずはどこからリダイレクトが発生しているのか探してみます。

wordpressでリダイレクトというと「wp_redirect()」関数ですね。

「wp_redirect(」でwordpress全体のソースコードを検索してみます。
※後ろの閉じ括弧は無しです。

いっぱい出てきました。

調べ方が間違ってました。
定義を探しているので「function wp_redirect(」として検索しなおします。

wp_redirect() 関数の中で、header("Location: $location", true, $status);が見つかりました。

この header() 関数の手前に次のコードを追加してみます。

echo $location . "\n";
echo '<pre>' . print_r( debug_backtrace(), true ) . '</pre>';
exit;

で、ブラウザから「/blog-2.html」にアクセスして確認してみます。
※追加したコードは忘れる前に削除して戻しておきます。

いっぱいでてきます。

とりあえず最初の [0] を見てみます。
今回は下記のような感じで出てきました。

Array
(
[0] => Array
(
[file] => ・・・/wp-includes/canonical.php
[line] => 516
[function] => wp_redirect
[args] => Array( ・・・ )

/wp-includes/canonical.phpの516行目から転送されているっぽいです。

ということで、そのあたりのソースを確認してみます。

意図せず転送されるパターンを調査

redirect_canonicalという関数に入ってるみたいです。

おもむろにこの関数の先頭にreturn;を入れてみます。

function redirect_canonical( $requested_url = null, $do_redirect = true ) {
return;

で、ブラウザから「/blog-2.html」にアクセスして確認してみます。
転送されません。ページ自体も問題なく表示できているようです。
確認できたら追加したreturn;は削除して元に戻しておきます。

redirect_canonical関数は結構長めですので、この中からどこに原因があるかあたりをつけていきます。
関数内の怪しそうなところに次のコードをいくつか追加して、ブラウザで表示してみます。

echo __LINE__; exit;

今回は「316」と表示されましたので、316行目を通っているようです。
追加したコードは削除して元に戻しておきます。

316行目の後ろにIF文が続きますので、同じようにコードを追加してみます。
調査範囲は10行程度に絞られたので、今回はすべてのIF文を通るように追加します。

ブラウザで確認すると「321」と表示されました。
追加したコードは削除して元に戻しておきます。

今回は320行目の次の個所が原因のようです。

$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');

次のようにして$addl_pathに何が入ってるか見てみます。

echo $addl_path; exit;
$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');

何も表示されません。$addl_pathは空文字のようです。
追加したコードは削除して元に戻しておきます。
ちなみに、289行目で$addl_path = '';と初期化されています。

今度は次のようにして$addl_pathに何が入れられてくるのか見てみます。

$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');
echo $addl_path; exit;

page/2が表示されました。

$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');
echo $addl_path; exit;

追加したコードは削除して元に戻しておきます。

ということで次のように$addl_pathに何も入らないようにしてみます。

$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');
$addl_path = '';

ページが表示され、期待しているものが出てきました。
追加したコードは削除して元に戻しておきます。

転送されないパターンを調査

転送されない場合はどうなっているかも確認しておきます。

redirect_canonical関数のreturn;の数カ所を次のようにします。

echo __LINE__; exit;
return;

で、ブラウザで今度は /blog.html にアクセスしてみます。
/blog.html は1ページ目にあたるURLでもともとcanonicalなので転送は発生しないページです。
477が表示されました。

477行目は次のようになっています。

if ( ! $redirect_url || $redirect_url == $requested_url ) {
return;
}

$redirect_urlはwordprpessが認識しているcanonicalなURLです。
$requested_urlはリクエストされたURL(アドレスバーのURL)です。

「canonicalなURLが設定されなかったか、もしくは、リクエストされたURLと一致するなら転送しない」ってことのようです。

では、$redirect_url がどこで設定されているか探してみます。
すぐ↑にあります

$redirect['path'] がどこで設定されているか探してみます。
何か所かあるので少しずつ絞っていきます。

477行目から上に見ていく、392行目に $redirect['path'] = がありました。
ここは条件分岐されてない絶対通る個所です。
このタイミングで何が入っているか見てみます。

echo $redirect['path'];
exit;

/blog-2.html/page/2

このタイミングでは既に変わってるようです。
もう少し上を見てみると次のようになっています。

if ( $redirect_url )
$redirect = @parse_url($redirect_url);

ここまでの調査で、320行目で page/2 が$redirect_urlに入ることは分かっています。
なので、このIF文の中に入って$redirectが設定されます。

解決

ここまで分かると後は解決に向けて考えるだけですね。

ちなみに上記までで調査のためにいじったファイルはすべて元に戻しておきます。
wordpress本体ファイルは直接いじらないようにしましょう。

$addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged');

こちらのコードで$addl_pathは空文字でたどり着いて、page/2が代入されていくことが分かっています。

user_trailingslashit関数の中で何かが起こっています。

function user_trailingslashit(で検索してみます。

user_trailingslashit関数の最後には次のようにフィルターがあるので書き換えできそうです。

return apply_filters( 'user_trailingslashit', $string, $type_of_url );

ので、functions.phpに次のコードを加えてみます。

add_filter( 'user_trailingslashit', function( $string, $type_of_url ){
echo '<pre>' . print_r( $string, true) . '</pre>';
echo '<pre>' . print_r( $type_of_url, true) . '</pre>';
exit;
return $string;
}, 10, 2);

次のように表示されました。
page/2
paged

ここまで来れば出来たようなものです。
次のように書き換えてみます。

add_filter( 'user_trailingslashit', function( $string, $type_of_url ){
if ( $type_of_url === 'paged' ) {
if (preg_match( '/\Apage\/[0-9]+\z/', $string)) {
return '';
}
}
return $string;
}, 10, 2);

ブラウザから /blog-2.html にアクセスすると期待通りに出てきました。

ただしまだ途中

これでOK!って言いたいところなんですがまだ続きます。

一見完了したように見えるんですが、ページ下部のページャーのところをクリックして、3ページ目、4ページ目に移動しようとしても出来なくなってしまいました。

この記事は「wordpressで複数ページあるとき「/page/番号」になるパーマリンクを変更する」の見出し「パーマリンク設定を追加する」から続いていました。

今回の記事は、元の記事の見出し「canonical のチェックで引っかからないようにする」に続きます。