カスタム投稿タイプを登録したのに、個別記事やアーカイブページにアクセスすると404 Not Foundが表示される――WordPress開発でよくあるトラブルです。「パーマリンクを再保存すれば直る」と聞いてやってみたけど解決しない、という方も多いのではないでしょうか。
この記事では、404になる5つの原因を体系的に整理し、それぞれの具体的な対処法とコード例を紹介します。さらに、リライトルールの仕組みを理解して根本から問題を解決できるデバッグ手法まで解説します。
この記事で分かること
- カスタム投稿タイプが404になる5つの原因と対処法
- パーマリンク再保存で直る理由と直らないケースの対応
- register_post_type() の rewrite パラメータの正しい設定
- リライトルールの仕組みとデバッグ方法
- アーカイブページ・ページネーションの404対策
- 本番環境での.htaccess トラブルシューティング
WordPressのリライトルールの仕組み
対処法の前に、WordPressがURLをどう解釈しているかを理解しておくと、404エラーの原因が格段にわかりやすくなります。
URLからクエリへの変換フロー
WordPressは、ユーザーがアクセスしたURLを内部的なクエリ(データベース問い合わせ)に変換して表示するページを決定します。この変換に使われるのがリライトルールです。
リクエスト処理フロー
URLを受け取る
/news/hello-world/
リライトルールとマッチング
news/([^/]+)/?$ → マッチ!
クエリ変数に変換
post_type=news&name=hello-world
データベースからデータ取得 → テンプレート表示
マッチしない or 記事が見つからない → 404
つまり404エラーが出るのは、STEP 2でマッチするルールがないか、STEP 4でデータが見つからないかのどちらかです。
リライトルールはどこに保存されている?
リライトルールはwp_optionsテーブルのrewrite_rulesオプションにシリアライズされた配列として保存されています。カスタム投稿タイプを登録すると、そのルールがこの配列に追加される必要がありますが、タイミングによっては追加されないままになることがあります。
原因1:リライトルールが更新されていない(最も多い)
カスタム投稿タイプが404になる最も多い原因がこれです。register_post_type()で新しい投稿タイプを登録しても、リライトルールは自動的には更新されません。
対処法A:パーマリンク設定の再保存(管理画面)
WordPress管理画面から、設定 → パーマリンクに移動し、何も変更せずに「変更を保存」ボタンをクリックします。
パーマリンク再保存の手順
- WordPress管理画面にログイン
- 設定 → パーマリンクを開く
- 何も変更せず「変更を保存」をクリック
- カスタム投稿の記事にアクセスして確認
なぜこれで直るのか?
「変更を保存」を押すと、内部で flush_rewrite_rules() が実行され、すべてのリライトルールが再生成されます。新しく登録したカスタム投稿タイプのルールもこのタイミングで追加されます。
対処法B:flush_rewrite_rules() をコードで実行
管理画面にアクセスできない場合や、プラグイン/テーマの有効化時に自動でルールを更新したい場合は、コードで実行できます。
functions.php – テーマ・プラグイン有効化時にフラッシュ
// テーマの有効化時にリライトルールを再生成 function mytheme_activate() { // 先にカスタム投稿タイプを登録 mytheme_register_post_types(); // リライトルールをフラッシュ flush_rewrite_rules(); } add_action( 'after_switch_theme', 'mytheme_activate' );
⚠ 注意:init フックで flush_rewrite_rules() を呼ばない
flush_rewrite_rules() はデータベースへの書き込みを伴う重い処理です。initフック内で毎回実行すると、ページアクセスのたびにリライトルールが再生成され、パフォーマンスが著しく低下します。
テーマやプラグインの有効化・無効化時のみ実行してください。
NG例:init で毎回フラッシュ(絶対にやらない)
// ❌ 毎リクエストで実行される → パフォーマンス低下 add_action( 'init', function() { register_post_type( 'news', [ ... ] ); flush_rewrite_rules(); // ← これが問題 }); // ✅ テーマの場合:init で登録 + after_switch_theme でフラッシュ add_action( 'init', 'mytheme_register_post_types' ); add_action( 'after_switch_theme', function() { mytheme_register_post_types(); flush_rewrite_rules(); }); // ✅ プラグインの場合:register_activation_hook を使う register_activation_hook( __FILE__, function() { myplugin_register_post_types(); flush_rewrite_rules(); });
原因2:register_post_type() の rewrite 設定が正しくない
パーマリンクを再保存しても解決しない場合は、register_post_type() のパラメータ設定に問題がある可能性が高いです。
rewrite パラメータの詳細
register_post_type() の rewrite 関連パラメータ
register_post_type( 'news', [ 'label' => 'ニュース', 'public' => true, // ▼ 個別記事の表示に必要 'publicly_queryable' => true, // false だと個別記事が404に // ▼ リライトルールの設定 'rewrite' => [ 'slug' => 'news', // URLのスラッグ 'with_front' => false, // パーマリンク接頭辞を含めない 'pages' => true, // ページネーションルールを生成 'feeds' => true, // フィードルールを生成 ], // ▼ アーカイブページの表示に必要 'has_archive' => true, // false だとアーカイブが404に ]);
よくある設定ミスと修正方法
public と publicly_queryable の違い
public => true だけでは不十分なケースがあります。public は複数のパラメータをまとめて設定するショートカットですが、個別に上書きされることがあります。
public が内部的に設定するパラメータ
// public => true は以下と同等 'publicly_queryable' => true, // フロントでクエリ可能か 'show_ui' => true, // 管理画面に表示するか 'show_in_nav_menus' => true, // ナビメニューに含めるか 'exclude_from_search' => false, // 検索結果に含めるか // ⚠ public => true でも個別に上書きすると404になる register_post_type( 'news', [ 'public' => true, 'publicly_queryable' => false, // ← これで個別記事が404に! ]);
原因3:スラッグの競合
カスタム投稿タイプのスラッグが、既存の固定ページや別の投稿タイプのスラッグと重複していると404になります。WordPressはリライトルールを優先順位順に評価するため、先にマッチした方が使われます。
競合が起きるパターン
対処法:スラッグを変更する
スラッグを変更して競合を回避
// ❌ 固定ページ「news」と競合 register_post_type( 'news', [ 'rewrite' => [ 'slug' => 'news' ], ]); // ✅ 方法1:rewrite slug を変更 register_post_type( 'news', [ 'rewrite' => [ 'slug' => 'latest-news' ], ]); // URL: /latest-news/記事スラッグ/ // ✅ 方法2:競合する固定ページを削除 or スラッグ変更 // 固定ページ「news」→「news-page」に変更
? 確認方法
管理画面の固定ページ一覧で、カスタム投稿タイプと同じスラッグの固定ページがないか確認してください。また、設定 → パーマリンクのカテゴリベースが空欄(デフォルト)の場合、カテゴリスラッグとの競合も起きやすくなります。
原因4:.htaccessの問題(Apache環境)
Apache環境では、.htaccessファイルがリライトルールの実行に不可欠です。このファイルに問題があると、WordPress全体(または特定のURL)で404が発生します。
mod_rewrite が無効
ローカル開発環境(XAMPP、MAMPなど)では、Apacheのmod_rewriteモジュールが無効になっていることがあります。
httpd.conf で mod_rewrite を有効化
# httpd.conf(Apache設定ファイル) # 以下の行のコメントを外す(先頭の # を削除) LoadModule rewrite_module modules/mod_rewrite.so # AllowOverride を None → All に変更 <Directory "/var/www/html"> AllowOverride All # ← None から All に </Directory>
.htaccessが破損・欠損している
WordPressのルートディレクトリに.htaccessがない、または内容が壊れている場合は、以下の内容で再作成します。
WordPress標準の .htaccess
# BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress
⚠ サブディレクトリにインストールしている場合
WordPressを /blog/ などのサブディレクトリにインストールしている場合は、RewriteBase を変更する必要があります。
例:RewriteBase /blog/
.htaccessの詳しい書き方は「301リダイレクトの書き方」でも解説しています。
Nginx環境の場合
Nginxは.htaccessを使いません。サーバー設定で以下のようなリライトルールが必要です。
Nginx のリライトルール
location / {
try_files $uri $uri/ /index.php?$args;
}
原因5:プラグイン・テーマの競合
複数のプラグインやテーマが同じ投稿タイプを登録しようとしたり、リライトルールを上書きしたりして競合が発生することがあります。
診断手順
プラグイン競合の切り分け手順
管理画面 → プラグイン → 一括操作で全て無効化
設定 → パーマリンク → 変更を保存
解消 → プラグインが原因 / 未解消 → テーマが原因の可能性
有効化のたびにパーマリンク再保存 → 404チェック
404が再発したプラグインが原因
よくある競合プラグイン
リライトルールのデバッグ方法
上記の対処法で解決しない場合は、リライトルールの中身を直接確認するデバッグが有効です。
方法1:WP_Rewrite オブジェクトで確認
functions.php に一時的に追加してリライトルールを確認
// 現在のリライトルールを出力(管理者のみ) add_action( 'wp_footer', function() { if ( ! current_user_can( 'manage_options' ) ) return; global $wp_rewrite; $rules = $wp_rewrite->wp_rewrite_rules(); // カスタム投稿タイプ「news」のルールだけフィルタ $news_rules = array_filter( $rules, function( $query ) { return strpos( $query, 'post_type=news' ) !== false || strpos( $query, 'news=' ) !== false; }); echo '<!-- Rewrite Rules for news: '; print_r( $news_rules ); echo ' -->'; });
ブラウザでページのソースを表示し、HTMLコメント内に出力されたルールを確認します。カスタム投稿タイプのルールが含まれていなければ、登録のタイミングかパラメータに問題があります。
方法2:リクエスト解析スニペット
404ページでマッチしたルールと解析結果を出力
// リクエストの解析結果を出力(一時的なデバッグ用) add_action( 'template_redirect', function() { if ( ! current_user_can( 'manage_options' ) ) return; if ( ! is_404() ) return; global $wp, $wp_query; error_log( '=== 404 Debug =========================' ); error_log( 'Request URI: ' . $_SERVER['REQUEST_URI'] ); error_log( 'Matched Rule: ' . $wp->matched_rule ); error_log( 'Matched Query: ' . $wp->matched_query ); error_log( 'Query Vars: ' . print_r( $wp_query->query_vars, true ) ); error_log( '==========================================' ); });
wp-content/debug.log(WP_DEBUG_LOG を有効にしている場合)にリクエスト解析の詳細が出力されます。Matched Rule が空ならマッチするルールがない、値があるのに404ならクエリの結果が空、と原因を切り分けられます。
方法3:Rewrite Rules Inspector プラグイン
コードを書かずにリライトルールを確認したい場合は、Rewrite Rules Inspectorプラグインが便利です。管理画面のツール → Rewrite Rulesから、現在登録されているすべてのリライトルールを一覧で確認できます。
Rewrite Rules Inspector の使い方
- プラグインをインストール・有効化
- 管理画面のツール → Rewrite Rulesを開く
- フィルターボックスにカスタム投稿タイプ名を入力
- 該当するルールが表示されれば登録は正常
- ルールがなければ、
register_post_type()の設定を見直す
アーカイブページの404対策
個別記事は表示されるのに、アーカイブページ(一覧ページ)だけ404になるケースがあります。
has_archive の設定
has_archive の設定例
register_post_type( 'news', [ 'public' => true, // ▼ アーカイブページを有効にする 'has_archive' => true, // URL: /news/ でアーカイブ一覧が表示される // カスタムスラッグにすることも可能 // 'has_archive' => 'news-archive', // URL: /news-archive/ でアーカイブが表示される 'rewrite' => [ 'slug' => 'news', 'with_front' => false ], ]);
テンプレート階層の確認
アーカイブが404にならないのにコンテンツが表示されない場合は、テンプレートファイルの問題です。WordPressは以下の優先順位でテンプレートを探します。
テンプレート優先順位
個別記事(single)
single-{post_type}.php → single.php → singular.php → index.php
アーカイブ(archive)
archive-{post_type}.php → archive.php → index.php
例えば、カスタム投稿タイプ「news」のアーカイブ用テンプレートを作る場合は、archive-news.php を子テーマに作成します。テンプレートファイルの詳しい使い方は「カスタム投稿タイプごとに異なるテンプレートを適用する方法」で解説しています。
ページネーション(/page/2/)の404対策
カスタム投稿タイプのアーカイブで、2ページ目以降にアクセスすると404になることがあります。これはWordPressのメインクエリの件数設定が原因です。
原因:メインクエリの件数が実際の件数を超えている
WordPress管理画面の設定 → 表示設定の「1ページに表示する最大投稿数」がデフォルト10件に設定されている場合、カスタム投稿タイプの記事が5件しかなければ/page/2/は存在しないため404になります。
しかし、テンプレート側でWP_Queryを使って独自にページネーションを実装している場合、メインクエリとのズレが問題になります。
対処法:pre_get_posts でメインクエリを調整
functions.php – ページネーション404の修正
/** * カスタム投稿タイプのアーカイブで表示件数を変更 * メインクエリの件数をテンプレートの表示件数と合わせる */ add_action( 'pre_get_posts', function( $query ) { // 管理画面・メインクエリ以外は除外 if ( is_admin() || ! $query->is_main_query() ) { return; } // カスタム投稿タイプ「news」のアーカイブページ if ( $query->is_post_type_archive( 'news' ) ) { $query->set( 'posts_per_page', 12 ); } });
? ポイント
is_main_query() のチェックは必須です。これがないと、サイドバーのウィジェットなどサブクエリにも影響してしまいます。また、管理画面の投稿一覧の件数も変わってしまうため、is_admin() チェックも忘れずに。WP_Queryの条件指定について詳しくは「WP_Queryで複雑な条件検索を実装する方法」を参照してください。
トラブルシューティングのフローチャート
404エラーの原因を効率的に特定するためのフローチャートです。上から順に確認してください。
設定 → パーマリンク → 変更を保存
→ 解決したら完了
public, publicly_queryable, has_archive, rewrite を確認→ 修正したらパーマリンク再保存
同名の固定ページ・カテゴリ・別のCPTがないか確認
→ スラッグを変更 → パーマリンク再保存
ファイルの存在確認、mod_rewrite有効化、AllowOverride All
→ .htaccessを修正 → Apache再起動
全プラグインを無効化 → 1つずつ有効化して特定
本記事のデバッグコードを使って原因を特定
完全なカスタム投稿タイプの登録コード
最後に、404エラーを防ぐためのベストプラクティスを盛り込んだ登録コードを紹介します。カスタム投稿タイプの基本的な追加方法は「カスタム投稿タイプを追加する方法」で詳しく解説しています。
functions.php – 404を防ぐカスタム投稿タイプ登録のベストプラクティス
/** * カスタム投稿タイプの登録 */ function mytheme_register_post_types() { $labels = [ 'name' => 'ニュース', 'singular_name' => 'ニュース', 'add_new' => '新規追加', 'add_new_item' => '新規ニュースを追加', 'edit_item' => 'ニュースを編集', 'view_item' => 'ニュースを表示', 'search_items' => 'ニュースを検索', 'not_found' => 'ニュースが見つかりません', 'not_found_in_trash' => 'ゴミ箱にニュースはありません', ]; $args = [ 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, // 明示的に true 'show_ui' => true, 'show_in_rest' => true, // ブロックエディタ対応 'has_archive' => true, // アーカイブページ有効 'hierarchical' => false, 'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'revisions', ], 'rewrite' => [ 'slug' => 'news', 'with_front' => false, // 接頭辞なし 'pages' => true, // ページネーション 'feeds' => true, // RSSフィード ], 'menu_icon' => 'dashicons-megaphone', 'menu_position' => 5, ]; register_post_type( 'news', $args ); } add_action( 'init', 'mytheme_register_post_types' ); /** * テーマ有効化時にリライトルールをフラッシュ */ function mytheme_rewrite_flush() { mytheme_register_post_types(); flush_rewrite_rules(); } add_action( 'after_switch_theme', 'mytheme_rewrite_flush' );
まとめ
カスタム投稿タイプの404エラー 対処チェックリスト
register_post_type() の設定を確認(public, publicly_queryable, has_archive, rewrite)
.htaccess の内容と mod_rewrite の有効化を確認(Apache環境)
flush_rewrite_rules() は有効化フックでのみ実行(init で毎回呼ばない)
カスタム投稿タイプの404エラーは、原因さえ特定できれば確実に解決できるトラブルです。まずはパーマリンク再保存を試し、それでもダメなら本記事のフローチャートに沿って原因を切り分けてください。
カスタム投稿タイプの基本的な追加方法は「カスタム投稿タイプを追加する方法」、アーカイブの作り方は「カスタム投稿の一覧ページを作成する方法」で解説しています。テンプレートの適用については「カスタム投稿タイプごとに異なるテンプレートを適用する方法」を参照してください。
また、独自の404ページを作りたい場合は「プラグイン無しで独自の404ページを作成する方法」、.htaccessでのリダイレクト設定については「301リダイレクトの書き方」、functions.phpの管理については「functions.phpを分割して管理する方法」も参考にしてください。
