[wp-trac] [WordPress Trac] #65407: Proposal: Add support for the No-Vary-Search HTTP response header

WordPress Trac noreply at wordpress.org
Thu Jun 4 12:43:15 UTC 2026


#65407: Proposal: Add support for the No-Vary-Search HTTP response header
----------------------------+------------------------------
 Reporter:  jonoaldersonwp  |       Owner:  (none)
     Type:  defect (bug)    |      Status:  new
 Priority:  lowest          |   Milestone:  Awaiting Review
Component:  General         |     Version:
 Severity:  normal          |  Resolution:
 Keywords:                  |     Focuses:  performance
----------------------------+------------------------------
Description changed by jonoaldersonwp:

Old description:

> This ticket proposes adding WordPress Core support for the `No-Vary-
> Search` HTTP response header.
>
> The goal is to give WordPress sites, plugins, themes, hosts, and
> performance tooling a consistent way to describe when URL query-string
> differences do not affect the returned response.
>
> This could help supporting browsers and cache layers to reuse responses
> more effectively - especially where URLs differ only by query parameter
> ordering or common non-render-affecting tracking parameters.
>
> This proposal has three parts:
>
> 1) Add a small API for setting and managing the `No-Vary-Search` response
> header.
> 2) Consider sending `No-Vary-Search: key-order` by default for front-end
> and wp-admin (and REST) responses.
> 3) Consider providing an opt-in mechanism for ignoring common marketing
> parameters, such as `utm_source`, `utm_medium`, `gclid`, `fbclid`, and
> similar parameters.
>
> This is intended as a low-risk, incremental performance enhancement.
>
> == MDN Doc ==
> https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/No-
> Vary-Search
>
> == Background ==
>
> Many WordPress pages can be reached through URLs with equivalent query
> strings in different orders.
>
> For example:
>
> `/?foo=1&bar=2`
> `/?bar=2&foo=1`
>
> In many cases, those URLs return the same response. However, caches and
> browser-level prefetch/prerender mechanisms may treat them as separate
> resources unless the response explicitly says otherwise.
>
> The `No-Vary-Search` response header allows a server to declare which
> parts of the query string do not affect the response. A conservative
> first step would be to declare that query parameter order does not affect
> the response by sending `No-Vary-Search: key-order`.
>
> This does not say that parameters should be ignored. It only says that
> the ordering of query parameters should not affect response equivalence.
>
> == Proposed API ==
>
> WordPress already has mechanisms for filtering response headers, but `No-
> Vary-Search` has structured semantics. A dedicated API would make it
> easier for Core, plugins, themes, and hosts to compose values safely.
>
> === Possible API shape: ===
>
> `wp_no_vary_search_add_directive( 'key-order' );`
> `wp_no_vary_search_add_params( array( 'utm_source', 'utm_medium' ) );`
> `wp_no_vary_search_remove_params( array( 'gclid' ) );`
> `wp_get_no_vary_search_header_value();`
>
> The exact API design is open for discussion. The main requirement is that
> multiple components should be able to contribute to the final header
> without overwriting each other or producing invalid syntax.
>
> This should also include a filter, for example:
>
> `$no_vary_search = apply_filters( 'wp_no_vary_search', $no_vary_search
> );`
>
> There should also be a filter to allow for disabling the header entirely.
>
> That would allow plugins, themes, hosts, and enterprise deployments to
> adjust or disable the header where needed.
>
> == Proposed default: `key-order` ==
>
> Core could set the following header by default: `No-Vary-Search: key-
> order`
>
> This is an intentionally conservative default. It does not collapse
> different query parameters. It only indicates that the order of those
> parameters should not be significant.
>
> In practice, it is hard to imagine many WordPress sites intentionally
> varying their response based only on the order of otherwise identical
> query parameters. If such cases exist, they are likely to be highly
> unusual, and the value should be filterable or removable.
>
> Potential benefits include:
>
> - Better browser reuse of prefetched or prerendered documents.
> - Better cache efficiency for URLs with reordered query strings.
> - Reduced duplicate cache entries at scale.
>
> This should apply to both front-end and wp-admin responses unless there
> is a reason to scope it more narrowly.
>
> == Optional opt-in: common marketing parameters ==
>
> A second, more ambitious option would be to provide a way for site
> owners, hosts, or plugins to opt in to ignoring common marketing and
> click-tracking parameters (e.g., `utm_*` keys).
>
> Many sites use these parameters only for client-side analytics,
> attribution, or marketing tools. In those cases, the server-rendered HTML
> is usually identical whether the parameter is present or absent.
>
> However, this should ''not'' be a default behaviour in Core without
> further discussion. Some sites may use these parameters server-side for
> personalisation, redirects, A/B testing, lead attribution, cache
> segmentation, or analytics logging.
>
> A safer approach would be:
>
> - Core provides the API.
> - Core defaults only to `key-order`.
> - Plugins, hosts, or site owners can opt in to ignoring recognised
> marketing parameters.
>
> == Interaction with Speculation Rules ==
>
> This proposal should be considered alongside the Speculation Rules API.
>
> Speculation Rules can include an `expects_no_vary_search` value, which
> tells the browser what `No-Vary-Search` value it should expect on the
> prefetched or prerendered response. This can help the browser decide
> whether an existing speculative load can satisfy a later navigation.
>
> If WordPress Core emits `No-Vary-Search`, then any Core, plugin, theme,
> or Performance Lab feature that emits speculation rules may need to
> account for it.
>
> This is also a reason for the proposed API to expose the computed header
> value before headers are sent.
>
> == Backwards compatibility and risk ==
>
> The proposed default of `key-order` should have a low backwards-
> compatibility risk because it does not ignore, remove, or normalise query
> parameters. It only declares that parameter ordering is not meaningful.
>
> The higher-risk behaviour is ignoring named parameters. That should be
> opt-in and filterable.
>
> == Potential edge cases to review ==
>
> - Sites that intentionally vary output based on query parameter order.
> - Plugins that parse raw query strings rather than using normalised
> request data.
> - REST API endpoints and custom endpoints that may have different cache
> semantics.
> - Admin screens that use query parameters heavily.
> - Login, registration, password reset, preview, customiser, and nonce-
> bearing URLs.
> - Hosts or CDNs that already set or transform No-Vary-Search.
> - Interaction with page caching, object caching, browser prefetching,
> prerendering, and speculation rules.
>
> == Implementation considerations ==
>
> A minimal implementation could:
>
> - Introduce a small internal representation of the No-Vary-Search
> directives.
> - Add a public API for adding, removing, and retrieving directives.
> - Add a filter for the final value.
> - Send No-Vary-Search: key-order by default for front-end responses.
> - Allow the header to be disabled or modified by plugins, themes, or
> hosts.
> - Add tests for header construction and default behaviour.
>
> == Open questions ==
>
> - Should key-order be emitted for front-end responses only initially, or
> also for wp-admin?
> - What should the API look like so multiple plugins can contribute
> safely?
> - Should Core maintain a canonical list of common marketing parameters?
> - Should there be a separate filter for the default Core value and the
> final emitted value?
> - How should this interact with current and future speculation rules
> support?
> - Should REST API responses be included, excluded, or separately
> filterable?
>
> ## Why this belongs in Core ##
>
> WordPress powers a large part of the web, and small improvements to
> default HTTP semantics can have an outsized impact.
>
> Because several independent systems may need to cooperate on the same
> header, including Core, performance plugins, caching plugins, hosts,
> CDNs, and speculation-rules generators, this benefits from a shared Core
> abstraction.

New description:

 This ticket proposes adding WordPress Core support for the `No-Vary-
 Search` HTTP response header.

 The goal is to give WordPress sites, plugins, themes, hosts, and
 performance tooling a consistent way to describe when URL query-string
 differences do not affect the returned response.

 This could help supporting browsers and cache layers to reuse responses
 more effectively - especially where URLs differ only by query parameter
 ordering or common non-render-affecting tracking parameters.

 This proposal has three parts:

 1) Add a small API for setting and managing the `No-Vary-Search` response
 header.
 2) Consider sending `No-Vary-Search: key-order` by default for front-end
 and wp-admin (and REST) responses.
 3) Consider providing an opt-in mechanism for ignoring common marketing
 parameters, such as `utm_source`, `utm_medium`, `gclid`, `fbclid`, and
 similar parameters.

 This is intended as a low-risk, incremental performance enhancement.

 == MDN Doc ==
 https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/No-
 Vary-Search

 == Background ==

 Many WordPress pages can be reached through URLs with equivalent query
 strings in different orders.

 For example:

 `/?foo=1&bar=2`
 `/?bar=2&foo=1`

 In many cases, those URLs return the same response. However, caches and
 browser-level prefetch/prerender mechanisms may treat them as separate
 resources unless the response explicitly says otherwise.

 The `No-Vary-Search` response header allows a server to declare which
 parts of the query string do not affect the response. A conservative first
 step would be to declare that query parameter order does not affect the
 response by sending `No-Vary-Search: key-order`.

 This does not say that parameters should be ignored. It only says that the
 ordering of query parameters should not affect response equivalence.

 == Proposed API ==

 WordPress already has mechanisms for filtering response headers, but `No-
 Vary-Search` has structured semantics. A dedicated API would make it
 easier for Core, plugins, themes, and hosts to compose values safely.

 === Possible API shape: ===

 `wp_no_vary_search_add_directive( 'key-order' );`
 `wp_no_vary_search_add_params( array( 'utm_source', 'utm_medium' ) );`
 `wp_no_vary_search_remove_params( array( 'gclid' ) );`
 `wp_get_no_vary_search_header_value();`

 The exact API design is open for discussion. The main requirement is that
 multiple components should be able to contribute to the final header
 without overwriting each other or producing invalid syntax.

 This should also include a filter, for example:

 `$no_vary_search = apply_filters( 'wp_no_vary_search', $no_vary_search );`

 There should also be a filter to allow for disabling the header entirely.

 That would allow plugins, themes, hosts, and enterprise deployments to
 adjust or disable the header where needed.

 == Proposed default: `key-order` ==

 Core could set the following header by default: `No-Vary-Search: key-
 order`

 This is an intentionally conservative default. It does not collapse
 different query parameters. It only indicates that the order of those
 parameters should not be significant.

 In practice, it is hard to imagine many WordPress sites intentionally
 varying their response based only on the order of otherwise identical
 query parameters. If such cases exist, they are likely to be highly
 unusual, and the value should be filterable or removable.

 Potential benefits include:

 - Better browser reuse of prefetched or prerendered documents.
 - Better cache efficiency for URLs with reordered query strings.
 - Reduced duplicate cache entries at scale.

 This should apply to both front-end and wp-admin responses unless there is
 a reason to scope it more narrowly.

 == Optional opt-in: common marketing parameters ==

 A second, more ambitious option would be to provide a way for site owners,
 hosts, or plugins to opt in to ignoring common marketing and click-
 tracking parameters (e.g., `utm_*` keys).

 Many sites use these parameters only for client-side analytics,
 attribution, or marketing tools. In those cases, the server-rendered HTML
 is usually identical whether the parameter is present or absent.

 However, this should ''not'' be a default behaviour in Core without
 further discussion. Some sites may use these parameters server-side for
 personalisation, redirects, A/B testing, lead attribution, cache
 segmentation, or analytics logging.

 A safer approach would be:

 - Core provides the API.
 - Core defaults only to `key-order`.
 - Plugins, hosts, or site owners can opt in to ignoring recognised
 marketing parameters.

 == Interaction with Speculation Rules ==

 This proposal should be considered alongside the Speculation Rules API.

 Speculation Rules can include an `expects_no_vary_search` value, which
 tells the browser what `No-Vary-Search` value it should expect on the
 prefetched or prerendered response. This can help the browser decide
 whether an existing speculative load can satisfy a later navigation.

 If WordPress Core emits `No-Vary-Search`, then any Core, plugin, theme, or
 Performance Lab feature that emits speculation rules may need to account
 for it.

 This is also a reason for the proposed API to expose the computed header
 value before headers are sent.

 == Backwards compatibility and risk ==

 The proposed default of `key-order` should have a low backwards-
 compatibility risk because it does not ignore, remove, or normalise query
 parameters. It only declares that parameter ordering is not meaningful.

 The higher-risk behaviour is ignoring named parameters. That should be
 opt-in and filterable.

 == Potential edge cases to review ==

 - Sites that intentionally vary output based on query parameter order.
 - Plugins that parse raw query strings rather than using normalised
 request data.
 - REST API endpoints and custom endpoints that may have different cache
 semantics.
 - Admin screens that use query parameters heavily.
 - Login, registration, password reset, preview, customiser, and nonce-
 bearing URLs.
 - Hosts or CDNs that already set or transform No-Vary-Search.
 - Interaction with page caching, object caching, browser prefetching,
 prerendering, and speculation rules.

 == Implementation considerations ==

 A minimal implementation could:

 - Introduce a small internal representation of the No-Vary-Search
 directives.
 - Add a public API for adding, removing, and retrieving directives.
 - Add a filter for the final value.
 - Send No-Vary-Search: key-order by default for front-end responses.
 - Allow the header to be disabled or modified by plugins, themes, or
 hosts.
 - Add tests for header construction and default behaviour.

 == Open questions ==

 - Should key-order be emitted for front-end responses only initially, or
 also for wp-admin?
 - What should the API look like so multiple plugins can contribute safely?
 - Should Core maintain a canonical list of common marketing parameters?
 - Should there be a separate filter for the default Core value and the
 final emitted value?
 - How should this interact with current and future speculation rules
 support?
 - Should REST API responses be included, excluded, or separately
 filterable?

 == Why this belongs in Core ==

 WordPress powers a large part of the web, and small improvements to
 default HTTP semantics can have an outsized impact.

 Because several independent systems may need to cooperate on the same
 header, including Core, performance plugins, caching plugins, hosts, CDNs,
 and speculation-rules generators, this benefits from a shared Core
 abstraction.

--

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/65407#comment:4>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list