2026年5月20日
2026年5月20日
WordPressの404ページをカスタマイズする方法:404.phpの作り方とリダイレクト・ログ
存在しない URL を開いたとき、WordPress はテーマの 404 用テンプレートでページを表示し、HTTP ステータスコード 404 を返します。初期のままでも動きますが、検索フォームや記事への道を置いておくと、読者がそのまま目的のページを探せます。
この記事のコードは、2026年11月時点の WordPress 6.9 と PHP 8.3 で構文を確かめています。
どのテンプレートが使われるか
| テーマの種類 | 404 のテンプレート | 編集する場所 |
|---|---|---|
| クラシックテーマ | テーマフォルダーの 404.php(無ければ index.php) |
ファイルを編集する |
| ブロックテーマ(Twenty Twenty-Five など) | templates/404.html |
「外観」→「エディター」→「テンプレート」→「404」 |
ブロックテーマなら、サイトエディターで 404 テンプレートを開き、「検索」ブロックや「最新の投稿」ブロックを置くだけで、コードを書かずに作り直せます。以下はクラシックテーマの例です。
市販のテーマを使っている場合は、テーマを直接編集すると更新で消えるため、子テーマに 404.php を置きます。子テーマの 404.php は親テーマのものより優先されます。テンプレートの選ばれ方は テンプレート階層の記事 で説明しています。
404.php を作る
テーマ(または子テーマ)のフォルダーに 404.php を作ります。.site-main などのクラス名は、使っているテーマの index.php に合わせてください。
<?php
/**
* 404 ページのテンプレート
*/
get_header();
?>
<main id="main" class="site-main error-404">
<header class="page-header">
<h1 class="page-title">ページが見つかりません</h1>
<p>お探しのページは移動・削除されたか、URL が間違っている可能性があります。</p>
</header>
<section class="error-404-search">
<h2>サイト内を検索する</h2>
<?php get_search_form(); ?>
</section>
<?php
$recent = new WP_Query(
array(
'posts_per_page' => 5,
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
)
);
if ( $recent->have_posts() ) :
?>
<section class="error-404-recent">
<h2>新着記事</h2>
<ul>
<?php
while ( $recent->have_posts() ) :
$recent->the_post();
?>
<li>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
<time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"><?php echo esc_html( get_the_date() ); ?></time>
</li>
<?php endwhile; ?>
</ul>
</section>
<?php
wp_reset_postdata();
endif;
?>
<section class="error-404-categories">
<h2>カテゴリー</h2>
<ul>
<?php
wp_list_categories(
array(
'orderby' => 'count',
'order' => 'DESC',
'show_count' => true,
'title_li' => '',
'number' => 10,
)
);
?>
</ul>
</section>
<p class="error-404-home">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">トップページへ戻る</a>
</p>
</main>
<?php
get_footer();
body には error404 というクラスが付くので、404 ページだけのデザインは body.error404 で指定できます。
ステータスコードが 404 になっているか確かめる
WordPress は、該当するページが無いと判断したときに自分でステータスコード 404 を送ります。404.php の中や functions.php で status_header( 404 ) を呼び直す必要はありません。
確かめるには、存在しない URL に curl でアクセスします(example.com は自分のドメインに置き換えます)。
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/no-such-page-12345/
404 と表示されれば正常です。200 と表示される場合は、プラグインやテーマが 404 をトップページなどへ転送していないか確かめてください。ページが無いのに 200 を返したり、すべての 404 をトップページへリダイレクトしたりすると、検索エンジンは「ソフト 404」として扱います。
検索エンジンはステータスコード 404 のページをインデックスしないので、404 ページに noindex のメタタグを追加する必要もありません。
旧 URL を新しい URL へリダイレクトする
WordPress が自動で行うリダイレクト
次の2つは、何も設定しなくても WordPress が行います。
- スラッグを変えた投稿: 公開済みの投稿のスラッグを変えると、古いスラッグが記録され、古い URL へのアクセスは新しい URL へ 301 でリダイレクトされます。
- URL の推測: 404 になった URL の末尾のスラッグと似た投稿があれば、その投稿へリダイレクトします。この推測を止めたい場合は、
do_redirect_guess_404_permalinkフィルターでfalseを返します。
自分で対応表を持つ
サイトの構成を変えて URL の規則がまとめて変わった場合などは、対応表でリダイレクトします。functions.php か、テーマに依存しないように wp-content/mu-plugins/ に置くファイルに書きます。
<?php
add_action( 'template_redirect', function () {
if ( ! is_404() ) {
return;
}
// クエリー文字列を除いたパス(例: /old-page/)
$path = wp_parse_url( $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH );
if ( ! is_string( $path ) ) {
return;
}
// 古いパス => 新しいパス
$redirects = array(
'/old-page/' => '/new-page/',
'/old-category/post/' => '/new-category/post/',
);
if ( isset( $redirects[ $path ] ) ) {
wp_safe_redirect( home_url( $redirects[ $path ] ), 301 );
exit;
}
} );
- WordPress をサブディレクトリー(
/blog/など)に置いている場合は、$pathに/blogが含まれるので、対応表の左側もそれに合わせます。 - 恒久的な移動は
301、一時的な移動は302を使います。 - 対応表が数十件を超える場合や、管理画面で編集したい場合は、Redirection プラグイン のほうが管理しやすくなります。Redirection には 404 の記録機能もあります。
404 になった URL を記録する
どの URL が 404 になっているかは、まずサーバーのアクセスログで確かめられます。サーバーのログを見られない場合は、PHP で記録します。
記録先は、Web から見えない場所(公開ディレクトリーの外)にします。wp-content の中に置くと、URL を知っている人が誰でもログを読めてしまいます。wp-config.php で記録先を決めておきます(パスは自分のサーバーに合わせます)。
// wp-config.php の「編集が必要なのはここまでです」の行より上に追加
define( 'MYTHEME_404_LOG', '/home/USER/logs/404.log' );
<?php
add_action( 'template_redirect', function () {
if ( ! is_404() || ! defined( 'MYTHEME_404_LOG' ) ) {
return;
}
// 改行などの制御文字を取り除き、長すぎる値を切る
$clean = function ( $value ) {
$value = preg_replace( '/[\x00-\x1F\x7F]/', '', (string) $value );
return substr( $value, 0, 500 );
};
$line = sprintf(
"%s\t%s\t%s\t%s\n",
gmdate( 'Y-m-d H:i:s' ),
$clean( $_SERVER['REQUEST_URI'] ?? '' ),
$clean( $_SERVER['HTTP_REFERER'] ?? '-' ),
$clean( $_SERVER['HTTP_USER_AGENT'] ?? '-' )
);
error_log( $line, 3, MYTHEME_404_LOG ); // 3 = 指定したファイルに追記する
}, 20 );
- 優先度を
20にして、リダイレクトの処理(優先度 10)のあとに記録しています。リダイレクトされた URL は記録されません。 - ログのフォルダーは事前に作り、PHP から書き込めるようにしておきます。
- 攻撃のための自動アクセスでも 404 は大量に出るので、ログは定期的に消すか、サーバーの logrotate などで回してください。
まとめ
- クラシックテーマは
404.php、ブロックテーマはサイトエディターの 404 テンプレートで作り直します。 - 404 ページには検索フォームや新着記事、カテゴリーへのリンクを置き、トップページへ戻れるようにします。
- WordPress は自分で 404 のステータスコードを返すので、
status_header( 404 )やnoindexを足す必要はありません。curl で404が返るかだけ確かめます。 - 旧 URL のリダイレクトは
template_redirectでwp_safe_redirect()を使うか、件数が多ければ Redirection プラグインを使います。