[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:34:38 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 | Keywords:
Focuses: performance |
----------------------------+-----------------------------
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 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 );`
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>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list