wordpress公式ロゴ

複数ページあるとき「/page/番号」になるパーマリンクを変更

状況

当サイトでは、トップページを固定ページにして投稿ページを「blog.html」の固定ページに割り当てています。

具体的には「管理画面」 > 「設定」 > 「表示設定」の「フロントページの設定」を「固定ページ」にし、「フロントページ」「投稿ページ」をそれぞれ設定しています。
「投稿ページ」に設定している固定ページのパーマリンクは「blog」です。

それから歴史的な事情により固定ページのURLの末尾は「.html」で終わるようにしています。

それが次のページです。
https://aulta.co.jp/blog.html

これはこれで良いんですが、2ページ目3ページ目・・・といくとURLがおかしなことになります。
https://aulta.co.jp/blog.html/page/2
https://aulta.co.jp/blog.html/page/3
こんな感じです。

やりたいこと

https://aulta.co.jp/blog.html/page/2
https://aulta.co.jp/blog.html/page/3

このようになっている2ページ目以降のURLを次のように変えたいです。

https://aulta.co.jp/blog-2.html
https://aulta.co.jp/blog-3.html

実施

現在の設定を確認

ここに記す作業は可能なら開発環境で行いましょう。本番と同じものをローカル環境に構築しておくとこういうとき安心して壊せます。
それと、このページと同じことを行って何か問題が発生しても弊社からは何もできませんので自己責任でお願いします。(有償サポートはお問合せください)

まずは今どうなっているのか調べていきます。
phpMyAdmin などから、次のSQLで調べられます。

SELECT * FROM `wp_options` WHERE `option_name` ='rewrite_rules'

でも、このままじゃよく分かりませんね。
ので、別の方法で確認してみます。

次のコードを functions.php の先頭に追加します。

echo '<pre>';
print_r(get_option( 'rewrite_rules' ));
echo '</pre>';
exit;

どこのページでも良いのでブラウザからアクセスすると中身が表示されます。
ブラウザ上ではやりづらいのでまるごとコピーしてテキストエディタに貼り付けます。

まずは投稿ページから変えようと思います。
現在のページは次のようになっています
https://aulta.co.jp/blog.html
https://aulta.co.jp/blog.html/page/2
https://aulta.co.jp/blog.html/page/3

テキストエディタから「/page/」 を探してみます。

いくつか候補があると思いますが、下記の形が上記URLにマッチする項目です。
※配列のキーは正規表現になっています。

[(.?.+?).html/page/?([0-9]{1,})/?$] => index.php?pagename=$matches[1]&paged=$matches[2]

パーマリンク設定を追加する

[(.?.+?).html/page/?([0-9]{1,})/?$] => index.php?pagename=$matches[1]&paged=$matches[2]

これを参考に次のような形を作ります。

add_action('init', function(){
add_rewrite_rule(
'(.?.+?).html/page/?([0-9]{1,})/?$',
'index.php?pagename=$matches[1]&paged=$matches[2]'
);
});

第一引数は配列のキー、第二引数は配列の値をそのまま持ってきています。

次に、第一引数(マッチする正規表現)を次のように変え、第三引数に'top'を加えます。
第二引数は変えません。

add_action('init', function(){
add_rewrite_rule(
'(.?.+?)-([1-9][0-9]*).html$',
'index.php?pagename=$matches[1]&paged=$matches[2]',
'top'
);
});

このコードはフロント側には不要のものなので、管理画面のときだけ実行されるよう is_admin()で条件判定を付け、最終的に次の形になります。

if (is_admin()) {
add_action('init', function(){
add_rewrite_rule(
'(.?.+?)-([1-9][0-9]*).html$',
'index.php?pagename=$matches[1]&paged=$matches[2]',
'top'
);
});
}

管理画面から、「設定」>「パーマリンク設定」に行き、何も変更せずそのまま保存します。
これで、追加したリライトルールが有効になります。
WP Rewriteのページには次のような説明があります

flush_rules関数はとても遅いので、実装するときにはすべてのページで実行されるinitフィルターで呼び出さないようにしてください。

「パーマリンク設定」で保存することで$wp_rewrite->flush_rules();と同じことを行っています。

これでブラウザから https://aulta.co.jp/blog-2.html にアクセスすると表示される!・・・と思いきや、「ページが見つかりません」になってしまいます。
※このあたりは長くなりますので「wordpressで謎の転送処理されているのを突き止める」に記します。

canonical のチェックで引っかからないようにする

blog-2.html でルール自体は認識されるようになったのですが、canonicalのチェックに引っかかって「wordpress自身が正しいと思ってるURL(/page/2の形式)」に転送されてしまいます。

次のコードをfunctions.phpに追記し、canonicalのチェックで転送されないようにします。

add_filter( 'user_trailingslashit', function( $string, $type_of_url ){
// 固定ページの投稿ページ
if ( is_home() ) {
if ( $type_of_url === 'paged' ) {
if (preg_match( '/\Apage\/([2-9]|[1-9][0-9]*)\z/', $string, $m)) {
return $m[1] . '.html';
}
}
}
return $string;
}, 10, 2);

これでブラウザから https://aulta.co.jp/blog-2.html にアクセスすると表示されるので「完成!」と言いたいところなのですがまだ続きます。

ページャのフォーマットを変える

一覧ページの下部にあるページャを見てみます。
1,2,3,4,・・・とページ番号が表示されているところです。
ここにマウスを乗せるか、デバッグツールなどでリンク先を確認してみます。

例えば3ページ目だと次のようなリンクになっています。
/blog-2.html/3.html
希望としては/blog-3.htmlになって欲しいところです。

次のコードをfunctions.phpに追記します。

add_filter( 'get_pagenum_link', function( $result ){
if ( is_home() ) {
if (preg_match( '/\A(http[s]:\/\/[^\/]+)\/(.+?)(-[0-9])?+\.html(\/[0-9]+\.html)?\z/', $result, $m)) {
return $m[1] . '/' . $m[2] . '%_%' . '.html';
}
}
return $result;
}, 10, 2);

これで確認してみると次のようになりました。
/blogpage/3.html
なので、まだ続きます。

2ページ目以降のリンクを「-数字.html」の形に変える

次のコードをfunctions.phpに追記します。

add_filter( 'paginate_links', function( $link ){
if ( is_home() ) {
if (preg_match( '/\A(http[s]:\/\/[^\/]+)\/([a-z0-9]+?)page\/([0-9]+)\.html\z/', $link, $m)) {
return $m[1] . '/' . $m[2] . '-' . $m[3] . '.html';
}
}
return $link;
}
);

これでブラウザから https://aulta.co.jp/blog-2.html にアクセスして確認してみます。
問題は見当たらないようで・・・問題ありました。

rel="canonical" を書き換える

ブラウザでソース表示してみると次が見つかりました。

<link <span class="html-attribute-name">rel</span>="<span class="html-attribute-value">canonical</span>" <span class="html-attribute-name">href</span>="https://aulta.co.jp/blog.html/page/3" />

これだとマズいですね。

上記「canonical のチェックで引っかからないようにする」で追加した個所に次のコードを追加します。
※「All In One SEO」プラグインを使っている関係で次のようになりますが、使っていない場合は別の方法になると思います。

if (preg_match( '/\A(.+)\.html\/page\/([2-9]|[1-9][0-9]*)\z/', $string, $m)) {
return $m[1] . '-' . $m[2] . '.html';
}

全体としては次のようになります。

add_filter( 'user_trailingslashit', function( $string, $type_of_url ){
if ( is_home() ) {
if ( $type_of_url === 'paged' ) {
if (preg_match( '/\Apage\/([2-9]|[1-9][0-9]*)\z/', $string, $m)) {
return $m[1] . '.html';
}
if (preg_match( '/\A(.+)\.html\/page\/([2-9]|[1-9][0-9]*)\z/', $string, $m)) {
return $m[1] . '-' . $m[2] . '.html';
}
}
}
return $string;}, 10, 2);

これでブラウザからソース表示で確認してみると次のように変わりました。

<link rel="canonical" href="https://aulta.co.jp/blog-2.html" />

固定ページに拡張子「.html」を付ける

一応、今回のカスタマイズにも関係ありますし、初めてこのページを見て知った方のために記しておきます。
次のコードをfunctions.phpに追記すると固定ページの拡張子を「.html」にすることができます。

add_action('init', function(){
/* @var WP_Rewrite $wp_rewrite */
global $wp_rewrite;
$wp_rewrite->use_trailing_slashes = false;
$wp_rewrite->page_structure = $wp_rewrite->root . '%pagename%.html';
});

※最近では理由が無い限りは拡張子を付ける意味は無くなってきていると思います。

カテゴリページなど他の一覧ページを確認

今回のカスタマイズは、管理画面「設定」>「表示設定」で「フロントページの表示」を「固定ページ」にして「投稿ページ」で設定したページについてです。

他のカテゴリページや年月ページなど一覧系ページに影響が出てないか確認しておきます。
ここまでのコードで、if ( is_home() ) で判定を入れているのはそういう意味でもあります。

ちなみに、他の一覧ページでの2ページ目以降は次のような形でデフォルトのままとします。
/page/2
/page/3

従来のURLから転送させる

ここまでの作業では次の形式のどちらでもアクセスできる状態になっています。

https://aulta.co.jp/blog-2.html
https://aulta.co.jp/blog-3.html
https://aulta.co.jp/blog.html/page/2
https://aulta.co.jp/blog.html/page/3

.htaccess に次を加えて転送するようにしておきます。

<IfModule mod_rewrite.c>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^blog\.html\/page\/([2-9]|[1-9][0-9]+)$ /blog-$1\.html [R=301,L]
</IfModule>

対応してみての感想

今回のこのカスタマイズを行うためにGoogleを使って似たようなことをやっている方がいないか色々探して回りました。
またwordpressのソースコードをあちこち見て回りました。

次の2点が分かりました。

  • web上で似たような情報が見つからない(検索の仕方が悪かったかもしれません)
  • wordpressはページングURLを自由にカスタマイズするようには出来ていない気がする
  • そもそもやり方を間違ってる気もする

本ページで行ったカスタマイズは見る人が見ると「無理やりやった感」を感じるかもしれません。

wordpressのソースコードを追いかけて、関連する個所のどこかでフックできないものかとapply_filtersをひたすら探しました。
wp-includes/canonical.phpの272行目で"$wp_rewrite->pagination_base/$page"を見つけたときには「これ無理じゃない?」とも思いました。

カスタマイズできるようになってたら、今のご時世ちょっと調べればスグに解決策が見つかりそうなものですし、まだwebに上がってないだけだったとしてもソースコードを見ればどうしたら良いかスグ分かるようになっていると思います。

でも、気になるところもあります。
時間の関係で深く追えてないのですが、違うやり方もできそうな可能性を見つけています。
もしかしたらそっちのやり方が正解かもしれません。
でも、今回は時間の都合もあり一旦ここまでにします。