毎回変えるか、一定時間固定するかを先に決める
おすすめ枠は更新ごとに変えたいですか? それとも、1時間や1日は同じ内容でよいでしょうか。前者ならWP_Queryのorderby => ‘rand’を使えます。後者なら、選んだ投稿IDを一定時間保存するほうが目的に合います。
この記事では、少数のおすすめ枠を前提に、対象の絞り方、ページ間や別枠との重複、負荷、固定方法を確認します。ページネーション付きのメイン一覧や、大量の候補へ無条件に使う方法ではありません。
更新で順番が変わるのは正常、重複・遅さは別に調べる
randは毎回必ず違う結果を返す保証ではありませんが、再取得で選ばれる記事や順番が変わるのは通常の動きです。 ページ間や別枠での重複、表示の遅さ、一定時間固定したいのに毎回変わる状態は分けて調べます。
| 症状 | よくある原因 | 最初に見る場所 |
|---|---|---|
| ページ更新ごとに順番が変わる | orderby=rand による通常の動き | $args の orderby |
| 2ページ目に同じ記事が出る | ランダム順とページネーションの相性が悪い | paged / paginate_links 周辺 |
| 表示が重い | 大量投稿に対して毎回ランダム取得している | posts_per_page / 絞り込み条件 |
| 別枠と同じ記事が出る | 除外IDを指定していない | post__not_in |
| ランダムなのに順番を固定したい | ランダム結果を保存していない | transient / post__in |
まずは最小のランダム表示で確認する
まず、公開済みの通常投稿から3件を表示します。クラシック子テーマのtemplate-parts/random-posts.phpへ保存し、表示したいテンプレートのメインループ内でthe_post()の後にget_template_part( ‘template-parts/random-posts’ );を呼んでください。投稿本文やfunctions.phpの直下へ貼るコードではありません。通常投稿以外ならpost_typeを登録キーへ置き換えます。
確認用として追加した場合は、終了後に呼び出しとファイルを撤去します。後半の固定版と同じ場所で同時に呼ぶと、別々の一覧が出るので、どちらか1つを使います。
現在のWP Query Builderでは、詳細設定で「post / publish / 3」、orderbyにrand、ignore_sticky_postsにtrueを入力し、自動高速化はオフにしました。次の$argsは、その実画面で生成された内容を変更せずに使っています。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
<?php$args = array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'orderby' => 'rand', 'ignore_sticky_posts' => true); $random_query = new WP_Query( $args ); if ( $random_query->have_posts() ) { echo '<ul>'; while ( $random_query->have_posts() ) { $random_query->the_post(); printf( '<li><a href="%1$s">%2$s</a> / ID: %3$d</li>', esc_url( get_permalink() ), esc_html( get_the_title() ), get_the_ID() ); } echo '</ul>';} else { echo '<p>表示できる投稿はありませんでした。</p>';} wp_reset_postdata();Builderが生成したのは$argsまでです。その後のnew WP_Query()、リンクと投稿IDの表示、0件時の文言、wp_reset_postdata()は、結果を確かめるために手動で追加しています。
orderby => ‘rand’で並べ、検証中は記事IDも表示します。これで似たタイトルではなく、同じ投稿が重複したかを判断できます。
同じ候補から取得した2回の表示を比べる
次の2枚は、公開済みの通常投稿23件を候補に、本文コードで3件ずつ表示したページです。1回目と再読み込み後の2回目で、選ばれた投稿IDを見比べます。各回の一覧内に同じIDが2つ出ることと、前の回にも出たIDが再登場することは別です。

候補は公開済みの通常投稿23件、取得は最大3件。この回はID62・60・57の順でした。

2回目はID57・62・14。ID57と62は再登場しています。条件は同じでも、毎回違う3件になる保証はありません。
更新前後の表示と負荷は、本番と分けた環境で確かめます。サーバー上でも試す場所が必要なら、XserverのPHP対応やバックアップ条件を確認できます。
よくある原因と確認順
2ページ目以降で順番が崩れる場合は、ページネーションの確認順へ進みます。別の表示枠と同じ記事が混ざる場合は、重複表示の原因を確認してください。投稿数が多いサイトではWP_Queryの高速化も合わせて確認してください。
件数、重複、ページネーション、候補集合の大きさ、固定化の順で確認します。
ランダム表示の確認順
- 1まず3件程度の最小クエリで orderby => 'rand' が効くか確認する
- 2タイトルだけでなく記事IDも表示して重複を確認する
- 3ページネーション付き一覧に使っていないか確認する
- 4大量投稿を毎回ランダム取得していないか確認する
- 5順番を一定時間固定したい場合はIDリストの保存を検討する
1. 更新するたびに順番が変わる
orderby => ‘rand’ を使っている場合、ページを更新するたびに順番が変わることは不自然ではありません。ランダム表示は、毎回同じ順番を保証するための指定ではないからです。
「今日だけ同じ順番にしたい」「1時間は同じおすすめ記事を出したい」という要件があるなら、毎回ランダム取得するのではなく、ランダムに選んだ記事IDのリストを一定時間保存する設計を考えます。
ページキャッシュやCDNが同じHTMLを返している間は、randを指定していても更新のたびに変わらないことがあります。 ログイン中と未ログイン時で結果が違う場合は、クエリだけでなくキャッシュの有効範囲も確認してください。すぐに表示を変えたいときは、対象ページのキャッシュを削除するか、キャッシュの保存時間と除外設定を見直します。サイト全体のキャッシュを無条件に無効化する必要はありません。
2. ページネーションと組み合わせている
ランダム表示とページネーションは相性に注意が必要です。1ページ目を開いた時と2ページ目を開いた時でランダム順が再計算されると、前のページに出た記事が次のページにも出たり、逆に見逃す記事が出たりします。
ページネーション付きのアーカイブでは、基本的には日付順、更新日順、ID順、メニュー順など、順番が安定する指定を使います。 どうしてもランダム順でページを分けたい場合は、先にランダムなIDリストを作って固定し、その順番に沿ってページ分割する設計が必要です。
たとえば候補をID10〜15の6件に絞り、posts_per_pageは3のまま、pagedを1・2にして別々に取得すると、ある回は1ページ目が10・11・12、2ページ目が14・15・12でした。ID12が重複し、ID13を見られない組み合わせです。これは再計算による一例で、常に同じIDが重複するわけではありません。
3. 表示件数だけを減らして安心している
ランダム表示は、少数の記事を見せる用途では便利ですが、posts_per_page => 3なら常に軽いわけではありません。返す投稿が3件でも、候補集合が大きければ、その候補をランダムに並べる処理は重くなり得ます。
表示数だけでなく、投稿タイプ、カテゴリー、タクソノミー、候補IDで母集団も絞ります。 「全投稿から3件」と「特定カテゴリーの50件から3件」では、同じ表示件数でも条件が違います。共通の上限値はないため、投稿数、呼び出し頻度、同時アクセスを検証環境で計測して判断します。meta_queryやtax_query、posts_per_page => -1との組み合わせは、より慎重に確認してください。
4. 別の表示枠と記事が重複している
現在の記事や先に表示した記事を除外するには、そのIDをpost__not_inへ渡します。次は通常投稿の個別ページ用の差分です。最初の$args定義直後、$random_query = new WP_Query( $args );より前へ置き、表示ループはそのまま使います。
引数や処理の一部分だけを示した例です。このまま単独で貼らず、テンプレート内のWP_Queryとループへ組み込んでください。
if ( is_singular( 'post' ) ) { $args['post__not_in'] = [ get_queried_object_id() ];}カテゴリーのアーカイブではget_queried_object_id()が投稿IDではなくタームIDになるため、個別投稿かを判定しています。先に表示した枠も除外するなら、その枠で取得した投稿IDを同じ除外配列へ追加します。
大量のIDをpost__not_inへ渡すと別の負荷が増えます。現在の記事や先に表示した数件程度に留め、大きな除外リストが必要なら候補集合を見直します。post__inで候補を限定している場合はpost__not_inを同時指定せず、先に候補配列から除外IDを取り除いてください。残りが空ならクエリを実行しない分岐も必要です。
5. 固定表示投稿の扱いを見落としている
通常の投稿一覧では、固定表示投稿が先頭に入ることがあります。ランダム表示の検証中に「なぜか同じ記事が目立つ」と感じる場合は、固定表示投稿の影響も確認してください。
ランダム枠で固定表示投稿を先頭へ移動したくない場合は、ignore_sticky_posts => trueを指定します。これは表示要件の指定であり、性能対策ではありません。また、固定表示投稿そのものを候補から除外する指定でもありません。完全に除外したい場合は、get_option( ‘sticky_posts’ )で得たIDをpost__not_inへ渡します。
候補の範囲と除外の方針を決めたら、WP Query Builderでランダム取得の$argsを組み立てられます。生成した条件を基本版の表示ループへ渡し、投稿IDを確認します。
少数のランダム枠の条件を作る
post_type、posts_per_page、orderbyから取得条件を作れます。選んだIDの保存・再利用やキャッシュの更新処理は、後半のコードで別に実装します。
選んだIDを最大1時間保存して再利用する
一定時間は同じおすすめを見せるなら、最初に選んだID列をTransientへ保存し、その順で再取得します。Transient APIは、計算結果などを有効期限付きで一時保存するWordPressの仕組みです。
次はtemplate-parts/random-posts-fixed.phpへ保存し、基本版の呼び出しをget_template_part( ‘template-parts/random-posts-fixed’ );へ交換する例です。条件は「公開済みの通常投稿から3件」で、同じキーを使う表示枠・閲覧者で結果を共有します。現在の記事の除外や、閲覧者ごとの選出は含んでいません。
最初のID取得は、Builderで基本版の条件にfields: idsとno_found_rows: trueを加え、自動高速化をオフにして生成しました。保存済みIDを動的に渡す2回目の取得、Transientの読込み・保存・削除、表示ループは手動で組み合わせています。
クラシックテーマのテンプレート(またはテンプレートパーツ)で、一覧を表示したい位置に置く例です。投稿本文やfunctions.phpへ、そのまま貼るコードではありません。
<?php$cache_key = 'wpq_random_post_ids_post_3_v1';$post_ids = get_transient( $cache_key ); if ( false === $post_ids ) { $args = array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 3, 'orderby' => 'rand', 'no_found_rows' => true, 'ignore_sticky_posts' => true, 'fields' => 'ids' ); $seed_query = new WP_Query( $args ); $post_ids = array_map( 'absint', $seed_query->posts ); if ( $post_ids ) { set_transient( $cache_key, $post_ids, HOUR_IN_SECONDS ); }} if ( $post_ids ) { $random_query = new WP_Query( [ 'post_type' => 'post', 'post_status' => 'publish', 'post__in' => $post_ids, 'orderby' => 'post__in', 'posts_per_page' => count( $post_ids ), 'ignore_sticky_posts' => true, ] ); if ( $random_query->have_posts() ) { echo '<ul>'; while ( $random_query->have_posts() ) { $random_query->the_post(); printf( '<li><a href="%s">%s</a> / ID: %d</li>', esc_url( get_permalink() ), esc_html( get_the_title() ), get_the_ID() ); } echo '</ul>'; } else { echo '<p>表示できる投稿はありませんでした。</p>'; } if ( $random_query->post_count < count( $post_ids ) ) { delete_transient( $cache_key ); } wp_reset_postdata();} else { echo '<p>表示できる投稿はありませんでした。</p>';}最初のクエリはfields => ‘ids’でIDだけを選びます。再表示ではpost__inとorderby => ‘post__in’を組み合わせ、保存順を維持します。空のpost__inは広い結果になることがあるため、IDが空ならクエリを実行しません。
保存中の投稿が削除・非公開化されると、その投稿は再取得で外れます。表示件数が保存ID数より少ない場合はキーを削除し、次の実行で選び直します。この実行中に不足分を補充するコードではありません。ページキャッシュが古いHTMLを返す場合の更新は別に必要です。
この例を撤去する時は、呼び出しとファイルを外し、自分の環境で作ったwpq_random_post_ids_post_3_v1だけをdelete_transient()で削除します。サイト全体のキャッシュを消す必要はありません。
保存中・消失後・0件で固定版の動きを比べる
同じ23件を候補にした固定版では、最初に選ばれたID15・79・55が、次のリクエストでも同じ順で表示されました。保存がなくなった後は、再び選出処理へ進みます。
| 状態 | 表示されたID | 動き |
|---|---|---|
| 保存なしで初回表示 | 15・79・55 | ランダム選出し、有効期限3600秒で保存 |
| 保存ありで再表示 | 15・79・55 | ランダム選出をせず保存順で取得 |
| 保存を削除して再表示 | 56・27・61 | 再びランダム選出する |
| 保存IDが存在しない投稿のみ | 0件 | 0件メッセージを出し、保存を削除 |
| 候補投稿が0件 | 0件 | 空のpost__inでは取得せず、0件メッセージを出す |
ここで比べたのは別リクエストでの再利用と、保存が途中で消えた場合の動きです。1時間ずっと保持されることや、ページキャッシュ・CDNの更新まで保証する例ではありません。
避けたい使い方
一覧全体、検索結果、カテゴリー、ページネーション付き画面では、読者が同じ結果を辿りにくくなります。関連記事の発見や古い記事の再露出など、用途を少数枠へ限定します。
orderby => ‘rand’は表示順をランダムにするための機能です。公平な抽選、セキュリティ用トークン、A/Bテストの固定割り当て、均等なローテーションには使わないでください。これらには再現性や割り当てを管理できる専用の仕組みが必要です。
ランダム表示とキャッシュの公式資料
ランダム順と取得条件はWP_Query、別ループ後の復元はwp_reset_postdata()、一定時間の固定はTransient APIの公式資料で確認します。
公式リンク
公式ドキュメント
ランダム表示の最終判断
- ランダム表示が本当に必要か、更新順や固定選出で代替できないか判断した
- ページ間の重複を許容するか決めた
- 対象件数と除外する投稿IDを先に絞った
- 順番を一定時間保つ場合の保存期間と更新契機を決めた
- 同じ記事の再表示をタイトルではなく投稿IDで確認した
この記事の検証情報
検証環境
- WordPress
- 7.1.4
- PHP
- 8.3.36
- テーマ
- wpq-media + DDEV
- 対象
- カスタム投稿 / taxonomy / meta_query
更新履歴
- 2026年10月7日 初回公開
この記事を活用する
あとで見返す・共有する
お気に入りに保存できます。役に立ったポイントは、いいねや共有で教えてください。


