[WordPress] カスタム投稿ページで２ページ目以降がrewrite_rulesで404でハマる

WordPressのテーマカスタマイズでよくあるエラーに、アーカイブページやカスタム投稿ページのアーカイブなどで２ページ目以降が404 not foundになってしまうというものがあります。

遭遇している人が多いので、あちこちで対処方法を解説してくださる方も多いため、どれかを試せば解消できるだろうと高を括ってましたが、見事にハマり、ソースコードを戻しても404から回復できなくなりました。

どういう手順でハマったかとその解消方法の記事です。

先に結論を述べておきます。

:::note info
* カスタム投稿ページを利用している場合はアーカイブページのURLから`category`を取るソースコードには注意しましょう
* ソースコードを戻しても404になってしまう場合は一旦違うテーマにしてみましょう
:::


## ことのはじまり

クライアント様から

「`https://ドメイン名/memo`のアドレスからアクセスすると、次のページが not found になるので修正して下さい！」

と連絡がありましたので、「承知しました！」と返答しました。

これが長い旅の始まり。

# アーカイブページが2つあった

サイトのトップカテゴリーは「memo」でアーカイブページのURLはデフォルトのままでしたので、

```
https://ドメイン名/category/memo/
```
がアーカイブページのURLになってました。


サイトのパーマリンクはカスタム構造で指定していて

```
/%category%/%post_id%/
```

になっています。そのため記事ページのURLは`https://ドメイン名/memo/541/`などになります。

サイト上から進むアーカイブページのリンクURLはすべて`category`がついていますが、記事のURLを削って加工すると、

```
https://ドメイン名/memo/
```

が出てきます。

このURLを表示すると、アーカイブページの1枚目として表示されていました。

そして、この`category`がついていないアーカイブページに設置してあったページネーションで2ページ目以降(`https://ドメイン名/memo/page/2/`)をクリックすると404になってしまう状況でした。

## カテゴリーページのURLから[category]を消す

URLに`category`は無い方が、記事ページとURLの整合性が良さそうだと思い、`category`は取ることにいたしました。

採用した方法はfunctions.phpにコードを追加してURLから`category` を消す方法です。以下のコードを使用しました。

```php:functions.php
function rem_cat_function($link) {
    return str_replace("/category/", "/", $link);
}

add_filter('user_trailingslashit', 'rem_cat_function');

function rem_cat_flush_rules() {
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

add_action('init', 'rem_cat_flush_rules');

function rem_cat_rewrite($wp_rewrite) {
    $new_rules = array('(.+)/page/(.+)/?' => 'index.php?category_name='.$wp_rewrite->preg_index(1).'&paged='.$wp_rewrite->preg_index(2));
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}

add_filter('generate_rewrite_rules', 'rem_cat_rewrite');
```

このコードはけっこうあちこちのサイトでも紹介されています。

このソースコードを追加したところ、アーカイブページのURLからは`category`の文字が消えて、

```
https://ドメイン名/memo/page/2/
```

このURLでアーカイブページの2ページ目が無事に表示されました。ここまでは順調でした。

## カスタム投稿ページやタグアーカイブ、日付アーカイブで404

サイト内ではカスタム投稿タイプ`artworks`を作り、カスタムタクソノミー`attribute`を作って、作品のギャラリーページを作ってありました。

先程のfunctions.phpへコードを入れて別のページも問題が無いか調べていたところ、こちらのカスタムタクソノミーのアーカイブページのURLの2ページ目に問題が発生しました。

```
https://ドメイン名/attribute/illustration-archives/page/2/
```

イラストアーカイブの２ページ目を確認したところ

**おおっと404！**

すぐさま、さっきのソースコードに問題があったのかと思い、functions.phpからソースコードをコメントアウトして、再度確認します。すると

**「404」の画面にいる**

リセットしても全滅した画面を見続けている気分になりました。このあと、カスタム投稿ページやカスタムタクソノミーの設定を見直したり、プラグインをONOFFしたりしましたが、まったく改善されず。

他のアーカイブも気になったので、タグや日付アーカイブをチェックしたところ、やはり２ページ目以降が表示されません。ソースコードは戻したのに404の状態が続き、.htaccessなども触ったりと、相当パニックになっていました。

## データベースで見つけた答え

WordPressの管理画面に、設定値を一覧で表示させる隠しページ「`options.php`」があります。

管理画面からURL指定で入るページなので、普段は使いませんが、ここのページを使ってデバッグする方法を紹介しているサイトがあり、ここで設定値をパラパラみていたところ`rewrite_rules`という項目がありました。


この項目は、管理画面からは`SERIALIZED DATA`となっていて、表示が隠され、触ることも出来ませんが、直接DATABASEを接続して`wp_options`テーブルを見ると、同じ項目名でデータを見ることが出来ます。

中身を見ると、URLのリダイレクトに関するルールがびっしりと書かれていました。

## ソースコードに立ち返る

ここでようやく`rewrite_rules`がデータベースで管理されていることに気が付きます。

パーマリンクの設定なども、ここに記憶されており、WordPressのURLは、WP_Rewriteのクラスを利用して、rewrite_rules内のルールに沿って、正規表現を使いクエリを書き直しています。

先ほどfunctions.phpに追加したソースコードを眺めてみます。

```php:functions.php
function rem_cat_flush_rules() {
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

add_action('init', 'rem_cat_flush_rules');

function rem_cat_rewrite($wp_rewrite) {
    $new_rules = array('(.+)/page/(.+)/?' => 'index.php?category_name='.$wp_rewrite->preg_index(1).'&paged='.$wp_rewrite->preg_index(2));
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}

add_filter('generate_rewrite_rules', 'rem_cat_rewrite');
```

`rem_cat_rewrite($wp_rewrite)`で既存のリダイレクトルールに新しく、`'(.+)/page/(.+)/?'`は`'index.php?category_name='.$wp_rewrite->preg_index(1).'&paged='.$wp_rewrite->preg_index(2)`へと書き直すルールが追加されています。

そして、このルールを`rem_cat_flush_rules()`でアクションフックを使い適応させるコードになっています。

つまり、

```
https://ドメイン名/memo/page/2/
↓
https://ドメイン名/index.php?category_name=memo&paged=2
```
へと変換されているわけですね。

これが、先程のカスタムタクソノミーのアーカイブだと、

```
https://ドメイン名/attribute/illustration-archives/page/2/
↓
https://ドメイン名/index.php?category_name=illustration-archives&paged=2
```

などになってしまいます。同様にtagの場合も

```
https://ドメイン名/tag/＊＊＊＊＊＊/page/2/
↓
https://ドメイン名/index.php?category_name=＊＊＊＊＊＊&paged=2
```

となってしまいました。どちらもcategory_nameではないので404ですね。

そしてこれは、**一旦ソースコードでDBに追加してしまうと、ソースコードをコメントアウトしても$wp_rewrite->flush_rules()を実行しない限りはrewrite_rulesに残り続ける**のです。

## どう対処したか

ではどうすれば、rewrite_rulesに書き込まれた内容を更新できるのかというと、**テーマを一旦別のテーマに切り替える**だけでテーマのrewrite_rulesが適応されて初期化されます。
（パーマリンク設定の保存でも治る事があるようですが、今回は治りませんでした。）

さらに先程のfunctions.phpの追加するrewirte_rulesをすこし書き換えました。

```php:functions.php
function rename_cat_rewrite($wp_rewrite)
{
    $new_rules = array(
        // 'attribute' と'tag','date'タクソノミーを除外するための条件を追加
        '((?!attribute|tag|date).+)/page/(.+)/?' => 'index.php?category_name=' . $wp_rewrite->preg_index(1) . '&paged=' . $wp_rewrite->preg_index(2)
    );
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_filter('generate_rewrite_rules', 'rename_cat_rewrite');

```

正規表現で'attribute' と 'tag'、'date'が含まれない場合のみに書き換えるルールへと変更しました。このコードを追加後に、テーマをいったんTwenty Twenty-Twoへ変更し、またオリジナルテーマに戻したところ、ようやく２ページ目以降が表示されるようになりました。

# まとめ

最初にも書いちゃいましたが

:::note info
* カスタム投稿ページを利用している場合はアーカイブページのURLから`category`を取るソースコードには注意しましょう
* ソースコードを戻しても404になってしまう場合は一旦違うテーマにしてみましょう
:::

WordPressは自由度が高いので、実装方法次第で様々なハマりポイントが発生しやすいですが先人たちの知恵にいつも助けられてます。

この記事も誰かのハマりポイントの解消に、役に立てることを祈りつつ。

# 参考

https://wporz.com/wordpress-category-url-hidden/#URLcategory-2

https://wordpress.dyno.design/404-error-custom-post-type/

https://www.560designs.com/memo/861.html