【保存版】WordPressでページネーションが効かない?「query_posts()」が招く落とし穴と正しい解決法

投稿一覧を出したいだけなのに、2ページ目で404になる。
「あれ、ちゃんとループ書いてるのに…?」
その原因、もしかして query_posts() を使っていませんか?

今回は、実際に筆者がハマった「ページネーションが効かないWordPress地獄」からの脱出劇を、経験ベースでわかりやすく解説します。

目次

🔥 事の発端:「固定ページテンプレートで一覧を出したいだけだった」

あるとき、こんなコードを書きました。

/*
Template Name: お知らせ
*/
<?php
$paged = get_query_var('paged') ?: 1;

query_posts(array(
  'post_type' => 'post',
  'posts_per_page' => 5,
  'paged' => $paged
));
?>

WordPressの固定ページで「お知らせ一覧」を表示したくて、何も考えずに query_posts() を使ったのです。

😱 表示されるけど…2ページ目に行くと 404 Not Found

URLは /news/page/2/
存在するはずなのに、なぜか404ページが出る

  • トップページでは投稿が表示される
  • the_posts_pagination() も書いてある
  • でも2ページ目が出ない。どこにも書いてない不具合。

🧠 原因:query_posts() がメインクエリを壊していた

WordPressには、URLに応じて自動的に生成される「メインクエリ」があります。

query_posts() はこれを強制的に上書きしてしまう関数です。

問題内容
query_posts() を使うとWordPress が作った元のクエリが破棄される
結果ページ数・カテゴリ・投稿タイプなどの判定ができなくなる
最終的にページネーションが壊れて404になる

⚠ 注意すべき点

🔄 query_posts() 非推奨の明確な理由とバージョン情報

query_posts() が非推奨とされているのは、WordPress 3.x 以降(2011年頃)から既知のベストプラクティスです。現在(WordPress 6.x時点)でも公式では「使用しないことを強く推奨」と明記されています。

理由としては:

  • メインクエリを書き換えてしまう → 条件分岐(is_category() など)が壊れる
  • グローバル変数を直接変更 → テンプレートタグやページネーションで不具合
  • 実装ミスの温床 → ページネーション失敗・404になる・表示件数が効かない等

将来的に互換性がなくなる可能性もあるため、新規開発では絶対に使わない方向が安心です。

代替案:WP_Querypre_get_posts を使用

🧭 get_query_var('paged') の補足

ページネーション用の $paged 変数を取得するために get_query_var('paged') を使いますが、これはアクセスしているページの種類によって動作が異なるため注意が必要です。

📌 ページ種類ごとの違い

ページ種別必要な変数解説
投稿一覧(ブログTOPなど)paged通常通り機能します
カテゴリー・タグ一覧paged通常通り機能します
固定ページ + カスタムテンプレートpageなぜか paged ではなく page に格納されることがある

✅ 対策(安全な取得方法)

$paged = get_query_var('paged') ? absint(get_query_var('paged')) : absint(get_query_var('page'));
$paged = $paged ? $paged : 1;

これで どのページタイプでも正しく現在のページ番号を取得できるようになります

🔧 パーマリンク設定にも注意

カスタム投稿タイプや独自テンプレートでページネーションを行う場合は、パーマリンク設定を「保存し直す」必要があることがあります。

設定場所:

  • WordPress管理画面 > [設定] > [パーマリンク] > 「変更を保存」

変更しなくてもOKですが、「保存ボタンを押す」ことで内部のリライトルールが再生成され、404エラーの原因を解消できることがあります。

😰 さらにミスを重ねた:category.phparchive.php が使われない?

その後、category-news.php を作成しても表示されないという問題にも直面しました。

原因は以下:

  • 固定ページテンプレートに Template Name: お知らせ と書いたことで、WordPressはそのテンプレートを優先してしまっていた。
  • category.phparchive.php が無視される。
  • URL構造とテンプレート階層がバラバラになり、思った通りに表示されない

✅ 解決方法まとめ(実体験)

誤り正しい方法
query_posts() でクエリを再定義WP_Query で新しく取得する
Template Name を使って一覧を表示category.phparchive.php を利用し、WordPressに任せる
固定ページ+クエリ再定義でページ送り投稿タイプごとに pre_get_posts を使って件数を変更する
パーマリンク設定を初期のまま「パーマリンク設定」で一度保存して再構築

💡 よくある思い込み vs 正しい理解

思い込み実はこうだった!
query_posts() は便利だから使ってOK使うとメインクエリを壊す。使ってはいけない
固定ページに Template Name: が必要一覧ページには不要。WordPressのテンプレート階層に任せる
ページネーションは the_posts_pagination() を入れればOKその前に正しいクエリが必要。WP_Query + max_num_pages を忘れずに
表示されてるからOKだと思った2ページ目で気づく地獄のトラップ

✅ 正しいコード例(WP_Query + ページネーション)

<?php
$paged = get_query_var('paged') ?: 1;

$args = array(
  'post_type' => 'blog', // カスタム投稿タイプ名
  'posts_per_page' => 5,
  'paged' => $paged
);

$blog_query = new WP_Query($args);
?>

<ul class="blog-list">
  <?php if ($blog_query->have_posts()) : while ($blog_query->have_posts()) : $blog_query->the_post(); ?>
    <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
  <?php endwhile; endif; ?>
</ul>

<?php
the_posts_pagination([
  'total' => $blog_query->max_num_pages
]);
wp_reset_postdata();
?>

💡 カスタム投稿で一覧表示+ページ送りするには

  • archive-{post_type}.php を作成する(例:archive-blog.php
  • テンプレート内で WP_Query を使わず、WordPressに任せる
  • 件数変更したいなら functions.php に以下を追記:
function blog_posts_per_page($query) {
  if (is_admin() || !$query->is_main_query()) return;
  if (is_post_type_archive('blog')) {
    $query->set('posts_per_page', 5);
  }
}
add_action('pre_get_posts', 'blog_posts_per_page');

☠️ query_posts() が絶対にダメな理由まとめ

理由詳細
メインクエリを壊すページネーション、URLマッチング、条件分岐が狂う
条件分岐が効かないis_category()is_archive() が false になる
パフォーマンスも悪化無駄にクエリを再生成して非効率
WordPress公式も非推奨公式ドキュメントにも記載あり

✅ まとめ:これからはこう使おう!

やりたいこと方法
一覧を表示したいcategory.phparchive.php を使う
固定ページに表示したいWP_Query で別インスタンスとして取得
表示件数を変えたいpre_get_postsfunctions.php に記述
ページネーションを正しく表示the_posts_pagination() + max_num_pages

🎯 最後に:この経験から学んだこと

  • query_posts() を安易に使うと、後で地獄を見る。
  • テンプレート階層を理解すれば WordPress はもっと快適になる。
  • ページネーションが壊れたら、まず疑うのは「クエリの書き方」

「動く」コードは書けても、「壊れない」コードを書くには理解が必要。
自分のミスを糧に、誰かのトラブルも防げたら嬉しいです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Web制作会社勤務、業界歴10年以上。現場を走り回ってきた制作者。
これからWeb制作を学ぶ人に寄り添いながら、実務で役立つ知識をわかりやすくシェアします。
「ゼロぐらいから学べる!」をモットーに、あなたの“がんばろ”を応援します!

目次