[wp-trac] [WordPress Trac] #65072: Registering a query_var called 'preview_nonce' stops previews from working.

WordPress Trac noreply at wordpress.org
Tue Apr 14 12:39:42 UTC 2026


#65072: Registering a query_var called 'preview_nonce' stops previews from working.
--------------------------+-----------------------------
 Reporter:  dsas          |      Owner:  dsas
     Type:  defect (bug)  |     Status:  assigned
 Priority:  normal        |  Milestone:  Awaiting Review
Component:  Query         |    Version:
 Severity:  normal        |   Keywords:
  Focuses:                |
--------------------------+-----------------------------
 **Summary**

 The static front page fallback in WP_Query::get_posts()
 [https://github.com/WordPress/wordpress-
 develop/blob/aa72dfed4432cfd875f77d2c475f772c6623cae5/src/wp-includes
 /class-wp-query.php#L2042 line ~2042] sets `is_page = true` and `is_home =
 false`, but does not update `is_singular`. This causes the query to
 generate incorrect SQL — using `post_type = 'post'` instead of `post_type
 = 'page'` — and return zero results.

 The equivalent check in `parse_query()` ([https://github.com/WordPress
 /wordpress-develop/blob/aa72dfed4432cfd875f77d2c475f772c6623cae5/src/wp-
 includes/class-wp-query.php#L1057 line ~1057]) correctly maintains the
 invariant by [https://github.com/WordPress/wordpress-
 develop/blob/aa72dfed4432cfd875f77d2c475f772c6623cae5/src/wp-includes
 /class-wp-query.php#L1135 recalculating is_singular at line 1135]. The
 `get_posts()`  backstop does not.

 **How to trigger**
 The backstop fires when `parse_query()` fails to detect the static front
 page because $this->query contains unexpected public query vars (anything
 not in the allowlist of preview, page, paged, cpage). This can happen when
 a plugin registers a query var that collides with a parameter WordPress
 core uses in preview URLs — for example, registering preview_nonce as a
 public query var.

 **Steps to reproduce**
  1. Register preview_nonce as a public query var: `add_filter(
 'query_vars', function ( $vars ) { $vars[] = 'preview_nonce'; return
 $vars; } );`
  2. Set a static front page in Settings → Reading
  3. Visit /?preview_id=<page_id>&preview_nonce=<valid_nonce>&preview=true

 Expected: The static front page content is displayed.
 Actual: The blog post index is displayed. The preview URL is redirected
 with preview=true stripped.

 **What happens internally**

   1. `parse_query()` line ~1057: The `array_diff` check fails because
 `preview_nonce` is not in the allowlist. `is_home` stays `true`, `is_page`
 stays `false`, `is_singular` stays `false`.

   2. `get_posts()` line ~2046: The backstop fires because `is_home` is
 `true` and `$query_vars['preview']` is `'true'`. It sets `is_page = true`,
 `is_home = false`, and `page_id` to the front page ID. **But it does not
 set `is_singular = true`.**

   3. `get_posts()` line ~2618: `$post_type_where` is correctly set to
 `post_type = 'page'` based on `is_page = true`.

   4. `get_posts()` line ~2705: The `! $this->is_singular` branch is
 entered. This branch builds its own combined post_type + post_status
 clause. Since `$post_type` is empty (line ~2003), line ~2713 defaults to
 `array('post')`, overriding the correct `post_type_where` from step 3.

   5. The resulting SQL is `WHERE ID = <page_id> AND post_type = 'post'`,
 which returns zero rows because the front page is a `page`.

   6. With zero results, `redirect_canonical` strips `preview=true` from
 the URL and redirects. On the second request, neither `parse_query()` nor
 the backstop can identify the request as the static front page.

 ** Note **

 I've fixed the plugin which exposed this issue, but the is_singular
 invariant violation in the backstop is a core issue.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/65072>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list