Make WordPress Core


Ignore:
Timestamp:
09/21/2026 01:05:38 PM (12 days ago)
Author:
mcsf
Message:

Support random content redirects via ?random qarg

Add opt-in support for random content redirects, so that a page request with the ?random query argument set will return a 302 redirect to a randomly selected published post.

The pool of eligible posts may be filtered with two additional query arguments:

  • random_post_type
  • random_cat_id

The feature may be opted into using:

add_filter( 'enable_random_content_redirect', '__return_true' );

Developed in: ​https://github.com/WordPress/wordpress-develop/pull/13022

Props mlaetitia.
Fixes #64498.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/query.php

    r63357 r63767  
    12411241
    12421242/**
     1243 * Redirects requests for random content to a randomly selected published post.
     1244 *
     1245 * Handles requests using the `random` query parameter, e.g. `example.com/?random`.
     1246 * The `random_post_type` parameter restricts the pick to a post type (default `post`),
     1247 * and the `random_cat_id` parameter restricts it to a category (for post types using
     1248 * the `category` taxonomy). Only published, non-password-protected content from
     1249 * viewable post types is considered, and only GET/HEAD requests are handled.
     1250 *
     1251 * Disabled by default. To enable:
     1252 *
     1253 *     add_filter( 'enable_random_content_redirect', '__return_true' );
     1254 *
     1255 * @since 7.2.0
     1256 */
     1257function wp_random_content_redirect() {
     1258        // The `random` parameter is a flag, present with or without a value.
     1259        if ( ! isset( $_GET['random'] ) ) {
     1260                return;
     1261        }
     1262
     1263        /**
     1264         * Filters whether random content redirects are enabled.
     1265         *
     1266         * @since 7.2.0
     1267         *
     1268         * @param bool $enabled Whether `?random` requests redirect to random content. Default false.
     1269         */
     1270        if ( ! apply_filters( 'enable_random_content_redirect', false ) ) {
     1271                return;
     1272        }
     1273
     1274        $request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( $_SERVER['REQUEST_METHOD'] ) : '';
     1275
     1276        if ( ! in_array( $request_method, array( 'GET', 'HEAD' ), true ) ) {
     1277                return;
     1278        }
     1279
     1280        $post_type = 'post';
     1281
     1282        if ( isset( $_GET['random_post_type'] ) ) {
     1283                $post_type = sanitize_key( wp_unslash( $_GET['random_post_type'] ) );
     1284        }
     1285
     1286        $post_type_object = get_post_type_object( $post_type );
     1287
     1288        if ( ! $post_type_object || ! is_post_type_viewable( $post_type_object ) ) {
     1289                return;
     1290        }
     1291
     1292        $args = array(
     1293                'post_type'              => $post_type,
     1294                'post_status'            => 'publish',
     1295                'has_password'           => false,
     1296                'posts_per_page'         => 1,
     1297                'orderby'                => 'ID',
     1298                'order'                  => 'ASC',
     1299                'ignore_sticky_posts'    => true,
     1300                'update_post_term_cache' => false,
     1301                'update_post_meta_cache' => false,
     1302        );
     1303
     1304        if ( isset( $_GET['random_cat_id'] ) ) {
     1305                $cat_id = (int) wp_unslash( $_GET['random_cat_id'] );
     1306
     1307                if ( $cat_id < 1 ) {
     1308                        return;
     1309                }
     1310
     1311                $args['cat'] = $cat_id;
     1312        }
     1313
     1314        // Pick via COUNT plus a random OFFSET rather than `ORDER BY RAND()`,
     1315        // which randomizes and sorts every candidate row on each request.
     1316        $query      = new WP_Query( $args );
     1317        $post_count = (int) $query->found_posts;
     1318
     1319        if ( $post_count > 1 ) {
     1320                $offset = wp_rand( 0, $post_count - 1 );
     1321
     1322                if ( $offset > 0 ) {
     1323                        $args['offset']        = $offset;
     1324                        $args['no_found_rows'] = true;
     1325                        $args['cache_results'] = false;
     1326
     1327                        $query = new WP_Query( $args );
     1328                }
     1329        }
     1330
     1331        if ( empty( $query->posts ) ) {
     1332                return;
     1333        }
     1334
     1335        $link = get_permalink( $query->posts[0] );
     1336
     1337        /**
     1338         * Filters the random content redirect URL.
     1339         *
     1340         * Returning a falsey value cancels the redirect.
     1341         *
     1342         * @since 7.2.0
     1343         *
     1344         * @param string $link The redirect URL.
     1345         */
     1346        $link = apply_filters( 'random_content_redirect_url', $link );
     1347
     1348        if ( ! $link ) {
     1349                return;
     1350        }
     1351
     1352        // Temporary redirect, so clients and intermediary caches do not cache the randomly picked target.
     1353        nocache_headers();
     1354        wp_safe_redirect( $link, 302 );
     1355        exit;
     1356}
     1357
     1358/**
    12431359 * Set up global post data.
    12441360 *
Note: See TracChangeset for help on using the changeset viewer.