2026年7月16日
2026年7月16日
WordPressの404ページをカスタマイズする方法
はじめに
WordPressで正しいURLにアクセスしているのに404エラーが表示される・テーマに404.phpを作成したのに反映されない・パーマリンクを変更したら既存の記事がすべて404になった・カスタム投稿タイプのアーカイブページが404になる・リダイレクト設定が意図通りに動かないといった問題は、WordPressのリライトルールとテンプレート階層を正しく理解することで解決できます。
症状・原因
- パーマリンク設定変更後にリライトルールがフラッシュされていない
- カスタム投稿タイプの
has_archiveやrewrite設定が不正確 AllowOverride Allが設定されておらず.htaccessのルールが無視されている- テーマに
404.phpが存在しないためデフォルトの404処理が使われている
解決手順
ステップ1:404の原因を診断する
# リライトルールの状態を確認
wp rewrite list --format=table
# パーマリンク設定を確認
wp option get permalink_structure
# リライトルールをフラッシュ(最初に試すべき操作)
wp rewrite flush --hard
# カスタム投稿タイプのリライト設定を確認
wp eval "
\$types = get_post_types([], 'objects');
foreach (\$types as \$type) {
if (!\$type->_builtin) {
echo \$type->name . ': has_archive=' . var_export(\$type->has_archive, true)
. ', rewrite=' . var_export(\$type->rewrite, true) . PHP_EOL;
}
}
"
# .htaccess の内容確認
wp eval "echo ABSPATH . '.htaccess';" | xargs cat
ステップ2:カスタム404ページを作成する
// テーマに 404.php を作成(テーマフォルダ直下に配置)
// 404.php の基本構造
get_header(); ?>
<main class="site-main">
<section class="error-404 not-found">
<header class="page-header">
<h1 class="page-title">ページが見つかりません</h1>
</header>
<div class="page-content">
<p>お探しのページは移動または削除された可能性があります。</p>
<?php get_search_form(); ?>
<div class="widget">
<h2>最近の投稿</h2>
<?php
$recent = new WP_Query([
'posts_per_page' => 5,
'no_found_rows' => true,
]);
if ($recent->have_posts()): ?>
<ul>
<?php while ($recent->have_posts()): $recent->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endwhile; wp_reset_postdata(); ?>
</ul>
<?php endif; ?>
</div>
</div>
</section>
</main>
<?php get_footer();
ステップ3:パーマリンク変更後の404を修正する
// functions.php: カスタム投稿タイプ登録時のリライト設定
add_action('init', function(): void {
register_post_type('product', [
'public' => true,
'has_archive' => true, // アーカイブページを有効化
'rewrite' => [
'slug' => 'products', // URLスラッグ
'with_front' => false, // フロントページのプレフィックスなし
'feeds' => true, // フィードURLを有効化
'pages' => true, // ページネーションを有効化
],
'supports' => ['title', 'editor', 'thumbnail'],
]);
});
// プラグイン有効化時にリライトルールをフラッシュ
register_activation_hook(__FILE__, function(): void {
// 投稿タイプ登録後にフラッシュ
flush_rewrite_rules();
});
// プラグイン無効化時にもフラッシュ
register_deactivation_hook(__FILE__, 'flush_rewrite_rules');
ステップ4:古いURLから新しいURLへリダイレクトする
// 旧URLから新URLへ301リダイレクト
add_action('template_redirect', function(): void {
if (!is_404()) {
return;
}
$request_uri = $_SERVER['REQUEST_URI'] ?? '';
// 旧スラッグから新スラッグへのマッピング
$redirects = [
'/old-category/old-post/' => '/new-category/new-post/',
'/products/' => '/shop/',
];
foreach ($redirects as $old => $new) {
if (str_starts_with($request_uri, $old)) {
wp_redirect(home_url($new), 301);
exit;
}
}
});
// redirect_canonical を無効化(カスタム処理と競合する場合)
// add_filter('redirect_canonical', '__return_false');
// 404ページの HTTP ステータスコードを確認・強制
add_filter('status_header', function(string $status_header, int $code): string {
if (is_404() && $code !== 404) {
return 'HTTP/1.1 404 Not Found';
}
return $status_header;
}, 10, 2);
ステップ5:404エラーをログに記録して分析する
// 404エラーをログに記録
add_action('template_redirect', function(): void {
if (!is_404()) {
return;
}
$uri = $_SERVER['REQUEST_URI'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? 'direct';
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
// ボットは除外してログを軽量化
$bot_patterns = ['Googlebot', 'bingbot', 'Slurp', 'DuckDuckBot'];
foreach ($bot_patterns as $bot) {
if (str_contains($user_agent, $bot)) {
return;
}
}
error_log(sprintf(
'[404] %s | Referer: %s | UA: %s',
$uri,
$referer,
substr($user_agent, 0, 100)
));
// カスタムデータベースに404を記録する場合
// update_option('404_log', array_merge(
// get_option('404_log', []),
// [['uri' => $uri, 'time' => time()]]
// ));
}, 99);
注意事項
flush_rewrite_rules()は毎リクエストで呼び出すと重大なパフォーマンス問題が発生します。必ずregister_activation_hook内か、管理画面のパーマリンク設定保存時(admin_initフック)のみで実行してくださいredirect_canonicalを完全に無効化するとSEOに悪影響を与える可能性があります
まとめ
WordPress 404問題の解決は①wp rewrite listでルール確認・wp rewrite flush --hardで最初にフラッシュ・カスタム投稿タイプのhas_archive/rewrite設定を確認、②テーマに404.phpを作成・検索フォームと最近の投稿を表示・ユーザーを迷子にしない導線設計、③カスタム投稿タイプにwith_front=falseとhas_archive=trueを設定・register_activation_hookでフラッシュ・deactivation_hookでもフラッシュ、④template_redirectフックで旧URLから新URLへ301リダイレクト・スラッグ変更時のマッピングを管理・status_headerフィルターで404ステータスを強制、⑤404エラーをログに記録して頻出URLを分析・ボットを除外してログを軽量化・リダイレクト対象を特定する手順で解決します。