タクソノミーの一覧ページだけが 404 になった
このブログで、目的から処理を探せる「逆引き」のカスタム投稿を作っていたときのことです。投稿タイプとタクソノミーを登録し、投稿タイプの URL を /my-reference/、タクソノミーの一覧ページの URL を /my-reference/cat/php/ のように、投稿タイプの下に置く形にしました。ところが、タクソノミーの一覧ページを開くと 404 になりました。
そこで、WordPress がこの URL をどのリライトルールで読んでいるかを調べました。記事の中の名前は例の名前(投稿タイプは my_reference、タクソノミーは my_reference_cat)にしています。
はじめに書いたコード
タクソノミーの rewrite の slug に、投稿タイプの URL の下になる my-reference/cat を入れていました。
function my_reference_register() {
register_post_type(
'my_reference',
array(
'public' => true,
'has_archive' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'my-reference', 'with_front' => false ),
)
);
register_taxonomy(
'my_reference_cat',
'my_reference',
array(
'public' => true,
'hierarchical' => true,
'rewrite' => array( 'slug' => 'my-reference/cat', 'with_front' => false ),
)
);
}
add_action( 'init', 'my_reference_register' );
原因:添付ファイルのリライトルールに先に当たる
WordPress は、URL をリライトルール(URL の正規表現の一覧)に上から順に当てて、最初に当たったもので中身を決めます。階層のない投稿タイプを登録すると、その一覧には 1 件ずつのページのルールのほかに、/my-reference/{投稿}/{添付ファイル}/ の形の、添付ファイルのページのルールも入ります。
タクソノミーのリライトルールは、投稿タイプのリライトルールのあとに並ぶので、/my-reference/cat/php/ はタクソノミーのリライトルールより先に、添付ファイルのリライトルールに当たります。その結果、「cat という投稿の、php という添付ファイル」として読まれ、そんな添付ファイルはないので 404 になっていました。
リライトルールの順番を確かめる
作業環境の WordPress を読み込む検証用スクリプトを作り、例の名前の投稿タイプとタクソノミーをメモリの中だけで登録してから、$wp_rewrite->rewrite_rules() でリライトルールの一覧を作りました。rewrite_rules() は作るだけで DB には保存しないので、作業環境のパーマリンクの設定は変わりません。そのうえで、WordPress が URL を読むときと同じように、ルールの正規表現に URL を上から順に当てて、最初に当たったものを出しています。
global $wp_rewrite;
$rules = $wp_rewrite->rewrite_rules(); // 作るだけで、DB には保存しない
$path = 'my-reference/cat/php';
$i = 0;
foreach ( $rules as $match => $query ) {
$i++;
if ( preg_match( "#^$match#", $path ) ) {
echo $i . ' ' . $match . ' => ' . $query . PHP_EOL;
break;
}
}
| 書き方 | 役目 |
|---|---|
$wp_rewrite->rewrite_rules() |
いま登録されている投稿タイプやタクソノミーから、リライトルールの一覧を作る |
preg_match( "#^$match#", $path ) |
WordPress が URL を読むときと同じ形で、ルールの正規表現に当てる |
break |
最初に当たったところで止める(WordPress も最初に当たったものを使う) |
作業環境で出た結果は、次のとおりです。番号はリライトルールの一覧の中の順番です。
| タクソノミーの slug | 開いた URL | 最初に当たったルール | 読まれ方 |
|---|---|---|---|
my-reference/cat |
/my-reference/cat/php/ |
121 番 my-reference/[^/]+/([^/]+)/?$ |
attachment=php(添付ファイル) |
my-reference-cat |
/my-reference-cat/php/ |
131 番 my-reference-cat/([^/]+)/?$ |
my_reference_cat=php(タクソノミーの一覧ページ) |
| どちらでも | /my-reference/some-post/ |
120 番 my-reference/([^/]+)(?:/([0-9]+))?/?$ |
my_reference=some-post(1 件のページ) |
はじめの形では、タクソノミーのリライトルール(127〜131 番)は一覧の後ろのほうにあり、その前の 121 番の添付ファイルのリライトルールに先に当たっていることが分かりました。試したあと、DB に保存されているルールが変わっていないことも、スクリプトの中で前後を比べて確かめています。
直し方:タクソノミーの URL を分ける
直し方はいくつかありますが、今回はタクソノミーの slug を my-reference-cat にして、投稿タイプの URL の下から出しました。タクソノミーの一覧ページは /my-reference-cat/php/ になり、添付ファイルのリライトルールとは形がぶつからなくなります。
| やり方 | よいところ | 気をつけること |
|---|---|---|
| タクソノミーの URL を分ける(今回のやり方) | slug を 1 つ変えるだけで、リライトルールの順番を気にしなくてよい | URL が投稿タイプの下に並ばない |
| タクソノミーを先に登録する | /my-reference/cat/php/ の形のままにできる |
登録の順番に頼るので、あとでコードを並べ替えたときに 404 に戻りやすい |
add_rewrite_rule() でルールを上に足す |
URL の形を自由に決められる | ルールを自分で書いて持ち続けることになる |
タクソノミーを先に登録するやり方も、検証用スクリプトで登録の順番を入れ替えてみたところ、タクソノミーのリライトルールが添付ファイルのリライトルールより上に並び、/my-reference/cat/php/ がタクソノミーの一覧ページとして読まれました。ただ、このやり方は登録の順番に頼っているうえ、cat という slug の投稿を作ると、その下の URL がタクソノミーのリライトルールに取られてしまいます。そのため今回は、いちばん簡単な、URL を分けるやり方にしました。
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'MY_REFERENCE_REWRITE_VERSION', '2' );
function my_reference_register() {
register_post_type(
'my_reference',
array(
'labels' => array( 'name' => '逆引き' ),
'public' => true,
'show_in_rest' => true,
'has_archive' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'my-reference', 'with_front' => false ),
'supports' => array( 'title', 'editor', 'excerpt', 'revisions' ),
)
);
register_taxonomy(
'my_reference_cat',
'my_reference',
array(
'labels' => array( 'name' => '逆引きの分類' ),
'public' => true,
'hierarchical' => true,
'show_in_rest' => true,
'show_admin_column' => true,
// my-reference/cat にすると、1 件のページの下の添付ファイルの決まりとぶつかる
'rewrite' => array( 'slug' => 'my-reference-cat', 'with_front' => false ),
)
);
}
add_action( 'init', 'my_reference_register' );
// 決まりを変えたときだけ、パーマリンクを作り直す
function my_reference_maybe_flush() {
if ( get_option( 'my_reference_rewrite_version' ) === MY_REFERENCE_REWRITE_VERSION ) {
return;
}
flush_rewrite_rules( false );
update_option( 'my_reference_rewrite_version', MY_REFERENCE_REWRITE_VERSION, false );
}
add_action( 'init', 'my_reference_maybe_flush', 20 );
| 書き方 | 役目 |
|---|---|
'slug' => 'my-reference-cat' |
タクソノミーの一覧ページの URL を、投稿タイプの URL の下から出す |
'with_front' => false |
パーマリンクの設定の前の部分(/archives/ など)を付けない |
MY_REFERENCE_REWRITE_VERSION |
リライトルールのバージョンの番号。slug を変えたら数を上げる |
get_option( … ) === … |
保存してある番号と同じなら、何もしないで戻る |
flush_rewrite_rules( false ) |
ルールを作り直して DB に保存する。false で .htaccess は書き換えない |
add_action( 'init', …, 20 ) |
投稿タイプとタクソノミーの登録(優先度 10)が終わってから作り直す |
パーマリンクの作り直しは、変えたときに 1 回だけ
投稿タイプやタクソノミーの URL を変えたときは、リライトルールを作り直さないと、新しい URL が 404 のままです。とはいえ flush_rewrite_rules() は重い処理で、ルールを DB に書き込むので、init で毎回呼ぶとページを開くたびに作り直すことになります。
そこで、option にバージョンの番号を入れておき、コードの番号と違うときだけ作り直して、番号を保存するようにしました。slug を変えたときは番号を 1 つ上げておけば、本番に入れたあとの最初の 1 回だけ作り直されます。作業環境で試すだけなら、管理画面の「設定 → パーマリンク」を開いて「変更を保存」を押すだけでも、ルールが作り直されます。
気をつけること
- 投稿タイプの URL の下に別の URL を置くと、1 件のページや添付ファイルのリライトルールとぶつかることがあります。URL の形を決めるときに、リライトルールの順番もあわせて確かめておきます
- slug を変えると、前の URL は 404 になります。公開したあとで変えるときは、前の URL からの転送も考えておきます
- パーマリンクを作り直しても 404 が直らないときは、リライトルールの順番を検証用スクリプトで出してみると、どこに当たっているかが分かります
まとめ
- 階層のない投稿タイプのリライトルールには、
/{slug}/{投稿}/{添付ファイル}/の形の添付ファイルのリライトルールが入る - タクソノミーの URL を投稿タイプの下に置くと、タクソノミーのリライトルールより先に添付ファイルのリライトルールに当たり、404 になることがある
- タクソノミーの slug を分ける(
my-reference-cat)のが、いちばん簡単な直し方 flush_rewrite_rules()は毎回呼ばず、option にバージョンの番号を入れて、変わったときに 1 回だけ呼ぶ


