【WordPress】カスタム投稿の記事が404になる原因と対処法|パーマリンク・リライトルール・デバッグまで完全解説

【WordPress】カスタム投稿の記事が404になる原因と対処法|パーマリンク・リライトルール・デバッグまで完全解説 WordPress

カスタム投稿タイプを登録したのに、個別記事やアーカイブページにアクセスすると404 Not Foundが表示される――WordPress開発でよくあるトラブルです。「パーマリンクを再保存すれば直る」と聞いてやってみたけど解決しない、という方も多いのではないでしょうか。

この記事では、404になる5つの原因を体系的に整理し、それぞれの具体的な対処法とコード例を紹介します。さらに、リライトルールの仕組みを理解して根本から問題を解決できるデバッグ手法まで解説します。

この記事で分かること

  • カスタム投稿タイプが404になる5つの原因と対処法
  • パーマリンク再保存で直る理由と直らないケースの対応
  • register_post_type() の rewrite パラメータの正しい設定
  • リライトルールの仕組みとデバッグ方法
  • アーカイブページ・ページネーションの404対策
  • 本番環境での.htaccess トラブルシューティング
スポンサーリンク

WordPressのリライトルールの仕組み

対処法の前に、WordPressがURLをどう解釈しているかを理解しておくと、404エラーの原因が格段にわかりやすくなります。

URLからクエリへの変換フロー

WordPressは、ユーザーがアクセスしたURLを内部的なクエリ(データベース問い合わせ)に変換して表示するページを決定します。この変換に使われるのがリライトルールです。

リクエスト処理フロー

STEP 1
URLを受け取る
/news/hello-world/
STEP 2
リライトルールとマッチング
news/([^/]+)/?$ → マッチ!
STEP 3
クエリ変数に変換
post_type=news&name=hello-world
STEP 4
データベースからデータ取得 → テンプレート表示
マッチしない or 記事が見つからない → 404

つまり404エラーが出るのは、STEP 2でマッチするルールがないか、STEP 4でデータが見つからないかのどちらかです。

リライトルールはどこに保存されている?

リライトルールはwp_optionsテーブルのrewrite_rulesオプションにシリアライズされた配列として保存されています。カスタム投稿タイプを登録すると、そのルールがこの配列に追加される必要がありますが、タイミングによっては追加されないままになることがあります。

原因1:リライトルールが更新されていない(最も多い)

カスタム投稿タイプが404になる最も多い原因がこれです。register_post_type()で新しい投稿タイプを登録しても、リライトルールは自動的には更新されません。

対処法A:パーマリンク設定の再保存(管理画面)

WordPress管理画面から、設定 → パーマリンクに移動し、何も変更せずに「変更を保存」ボタンをクリックします。

パーマリンク再保存の手順

  1. WordPress管理画面にログイン
  2. 設定 → パーマリンクを開く
  3. 何も変更せず「変更を保存」をクリック
  4. カスタム投稿の記事にアクセスして確認

なぜこれで直るのか?
「変更を保存」を押すと、内部で 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に
]);

よくある設定ミスと修正方法

症状 原因 修正方法
個別記事が404 publicly_queryablefalse true に変更
アーカイブが404 has_archivefalse true に変更
URLに /blog/news/ と余計なパスが入る with_fronttrue(デフォルト) with_front => false に設定
rewrite => false にしたら全部404 リライトルールが生成されない rewrite を配列で指定
ページネーション(/page/2/)が404 pagesfalse pages => true に設定
?post_type=news でも表示されない query_varfalse query_var => true に設定(またはデフォルトのまま)
原因不明の不具合が多発 投稿タイプ名が20文字超 or ハイフン含む 英小文字・アンダースコアのみ、20文字以内に

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」 + CPTスラッグ「news」 CPTの個別記事が404
カテゴリと同名のスラッグ カテゴリ「event」 + CPTスラッグ「event」 CPTアーカイブまたはカテゴリが404
別のCPTと同名のスラッグ CPT-A「works」 + CPT-B「works」 後から登録した方が404

対処法:スラッグを変更する

スラッグを変更して競合を回避

// ❌ 固定ページ「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:プラグイン・テーマの競合

複数のプラグインやテーマが同じ投稿タイプを登録しようとしたり、リライトルールを上書きしたりして競合が発生することがあります。

診断手順

プラグイン競合の切り分け手順

1. すべてのプラグインを無効化
管理画面 → プラグイン → 一括操作で全て無効化
2. パーマリンクを再保存
設定 → パーマリンク → 変更を保存
3. 404が解消されたか確認
解消 → プラグインが原因 / 未解消 → テーマが原因の可能性
4. プラグインを1つずつ有効化
有効化のたびにパーマリンク再保存 → 404チェック
404が再発したプラグインが原因

よくある競合プラグイン

プラグインカテゴリ 競合の理由 対処法
キャッシュプラグイン 古いリライトルールがキャッシュされる キャッシュをクリア → パーマリンク再保存
SEOプラグイン パーマリンク構造を独自に変更する SEOプラグインのURL設定を確認
多言語プラグイン(WPML等) 言語ごとにリライトルールを追加 言語設定でURLフォーマットを確認
カスタム投稿タイプ管理プラグイン CPT UIなどが同名の投稿タイプを登録 重複登録がないか確認

リライトルールのデバッグ方法

上記の対処法で解決しない場合は、リライトルールの中身を直接確認するデバッグが有効です。

方法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.logWP_DEBUG_LOG を有効にしている場合)にリクエスト解析の詳細が出力されます。Matched Rule が空ならマッチするルールがない、値があるのに404ならクエリの結果が空、と原因を切り分けられます。

方法3:Rewrite Rules Inspector プラグイン

コードを書かずにリライトルールを確認したい場合は、Rewrite Rules Inspectorプラグインが便利です。管理画面のツール → Rewrite Rulesから、現在登録されているすべてのリライトルールを一覧で確認できます。

Rewrite Rules Inspector の使い方

  1. プラグインをインストール・有効化
  2. 管理画面のツール → Rewrite Rulesを開く
  3. フィルターボックスにカスタム投稿タイプ名を入力
  4. 該当するルールが表示されれば登録は正常
  5. ルールがなければ、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}.phpsingle.phpsingular.phpindex.php

アーカイブ(archive)

archive-{post_type}.phparchive.phpindex.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エラーの原因を効率的に特定するためのフローチャートです。上から順に確認してください。

Q1. パーマリンクを再保存した?
設定 → パーマリンク → 変更を保存
→ 解決したら完了
↓ まだ404
Q2. register_post_type() の設定は正しい?
public, publicly_queryable, has_archive, rewrite を確認
→ 修正したらパーマリンク再保存
↓ まだ404
Q3. スラッグが固定ページ等と競合していない?
同名の固定ページ・カテゴリ・別のCPTがないか確認
→ スラッグを変更 → パーマリンク再保存
↓ まだ404
Q4. .htaccessは正常?(Apache環境)
ファイルの存在確認、mod_rewrite有効化、AllowOverride All
→ .htaccessを修正 → Apache再起動
↓ まだ404
Q5. プラグインの競合?
全プラグインを無効化 → 1つずつ有効化して特定
↓ まだ404
Q6. デバッグスニペットでリライトルールを確認
本記事のデバッグコードを使って原因を特定

完全なカスタム投稿タイプの登録コード

最後に、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環境)
✅ プラグインの競合を切り分け(全無効化 → 1つずつ有効化)
flush_rewrite_rules() は有効化フックでのみ実行(init で毎回呼ばない)

カスタム投稿タイプの404エラーは、原因さえ特定できれば確実に解決できるトラブルです。まずはパーマリンク再保存を試し、それでもダメなら本記事のフローチャートに沿って原因を切り分けてください。

カスタム投稿タイプの基本的な追加方法は「カスタム投稿タイプを追加する方法」、アーカイブの作り方は「カスタム投稿の一覧ページを作成する方法」で解説しています。テンプレートの適用については「カスタム投稿タイプごとに異なるテンプレートを適用する方法」を参照してください。

また、独自の404ページを作りたい場合は「プラグイン無しで独自の404ページを作成する方法」、.htaccessでのリダイレクト設定については「301リダイレクトの書き方」、functions.phpの管理については「functions.phpを分割して管理する方法」も参考にしてください。