惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
K
Kaspersky official blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Spread Privacy
Spread Privacy
T
Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
V
Vulnerabilities – Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Help Net Security
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
小众软件
小众软件
Cloudbric
Cloudbric
The Hacker News
The Hacker News
阮一峰的网络日志
阮一峰的网络日志
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
Forbes - Security
Forbes - Security
Martin Fowler
Martin Fowler
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
AWS News Blog
AWS News Blog
Stack Overflow Blog
Stack Overflow Blog
N
News | PayPal Newsroom
P
Privacy & Cybersecurity Law Blog
TaoSecurity Blog
TaoSecurity Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园_首页
A
Arctic Wolf
www.infosecurity-magazine.com
www.infosecurity-magazine.com
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Scott Helme
Scott Helme
T
Tor Project blog
S
Secure Thoughts
Know Your Adversary
Know Your Adversary
P
Proofpoint News Feed
M
MIT News - Artificial intelligence
博客园 - 司徒正美
T
Threat Research - Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
Schneier on Security
Schneier on Security
B
Blog RSS Feed
AI
AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Attack and Defense Labs
Attack and Defense Labs
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
Hacker News: Ask HN
Hacker News: Ask HN

Adactio

June 16th, 2026, 11:12am June 16th, 2026, 10:16am Enhancing with CSS Grid Lanes How building an HTML-first site doubled our users overnight Speaking in Dublin June 13th, 2026, 9:09pm A tale of two browsers Sarah Canary by Karen Joy Fowler June 12th, 2026, 5:59pm The Field Guide to CSS Grid Lanes June 12th, 2026, 12:24pm June 12th, 2026, 8:58am June 11th, 2026, 6:22pm June 11th, 2026, 6:20pm June 10th, 2026, 6:25pm June 10th, 2026, 10:14am June 9th, 2026, 8:50pm June 9th, 2026, 1:55pm June 8th, 2026, 7:45pm June 8th, 2026, 4:32pm Amsterdamming June 5th, 2026, 3:16pm June 4th, 2026, 8:21pm June 2nd, 2026, 8:37pm 25 years of The Session Happy Monday everyone, and let's talk about gender and ethnicity ratios at tech events. AI and the Rise of Mediocrity May 28th, 2026, 7:24pm Picture at an exhibition May 27th, 2026, 9:41pm May 27th, 2026, 1:52pm May 25th, 2026, 8:03pm Gaeltacht cois Tamaise 2026 May 25th, 2026, 3:07pm May 23rd, 2026, 8:06pm May 23rd, 2026, 8:31am May 22nd, 2026, 3:38pm May 22nd, 2026, 8:19am May 22nd, 2026, 7:22am May 21st, 2026, 8:19pm Brigid by Kim Curran May 20th, 2026, 7:12pm The value is in the difficulty - Annotated May 17th, 2026, 6:21pm May 15th, 2026, 4:19pm Tito as Gaeilge The closing talks at UX London 2026 Three things about data Native Apps Should Be Avoided Whenever Possible — No One's Happy WebKit Features for Safari 26.5 May 11th, 2026, 4:17pm I knew my writing students were using AI. Their confessions led to a powerful teaching moment | Micah Nathan Gideon The Ninth by Tamsyn Muir May 8th, 2026, 3:55pm May 7th, 2026, 9:55am May 7th, 2026, 7:49am The schedule for UX London 2026 Google’s Prompt API Reminder: You Can Stitch Together Lots of Little HTML Pages With Navigations For Interactions Netizen | Derek Sivers April 30th, 2026, 7:59pm April 29th, 2026, 8:12pm April 27th, 2026, 7:47pm April 25th, 2026, 12:03pm April 25th, 2026, 12:00pm April 25th, 2026, 8:03am April 24th, 2026, 7:57pm April 24th, 2026, 5:12pm Two Paradigms for Enhancing HTML Tags Summary punishment It's Not AI. It's FOMOnetization. Alistair Davidson / validation-enhancer · GitLab Never Lose Form Progress Again :: Aaron Gustafson Dilation Expansion artifacts April 19th, 2026, 6:03pm Finn Mac Cool by Morgan Llywelyn April 17th, 2026, 7:58am April 16th, 2026, 6:42pm Threat models No-stack web development Design and Engineering, As One · Matthias Ott April 14th, 2026, 7:21am April 13th, 2026, 7:47pm April 11th, 2026, 8:39am April 10th, 2026, 5:36pm My salary history Conference organising in 2026 April 7th, 2026, 8:32pm TinyStart AI Might Be Our Best Shot At Taking Back The Open Web | Techdirt April 6th, 2026, 12:46pm The AI Great Leap Forward April 4th, 2026, 6:42pm April 3rd, 2026, 5:27pm April 2nd, 2026, 8:58pm April 2nd, 2026, 4:32pm Web Day Out - 12 March 2026 Mistrust HTML Video Poster Image: Enable Responsive Images and ALT Text for Poster
Better Browser Caching with No-Vary-Search
Harry Roberts · 2026-05-08 · via Adactio

(last updated on )

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. The Problem We’re Solving
  2. What No-Vary-Search Does
  3. A Simple Example
  4. No-Vary-Search Syntaxes
    1. Ignore Specific Parameters
    2. Ignore All Parameters
    3. Ignore Everything Except Certain Parameters
    4. Ignore Parameter Order
    5. Combining No-Vary-Search Rules
  5. A Small Syntax Gotcha
  6. A Small Debugging Gotcha
  7. Unknown Params Should Bust the Cache
  8. Use It Carefully
  9. A Nice Fit for Messy Real-World URLs

I’ve written, spoken, and generally gone on at length about caching for years now, but a newer addition to the conversation is No-Vary-Search: an HTTP response header that helps us solve a surprisingly common problem with cache keys in HTTP cache (or browser cache).

The short version is, URLs that are materially the same often fail to reuse the same cached response simply because their query strings differ. Sometimes that difference matters to the content and, therefore, the end user. For example:

  • ?colour=red
  • ?colour=blue

And sometimes, it really doesn’t matter at all:

  • ?utm_source=google
  • ?utm_source=chatgpt

The former are very likely different pages, or at least pages that ought to produce different content. We would not want them cached under the same key.

The latter should, in almost every sane setup, return the exact same HTML. They are the same page, just with different tracking baggage attached.

And yet, to an HTTP cache, different query strings traditionally equate to completely different URLs, which means different cache entries. That is wasteful.

Need Some Help?

I help companies find and fix site-speed issues. Performance audits, training, consultancy, and more.

The Problem We’re Solving

By default, caches are cautious. If the URL differs, the cache key differs, and, in a cautious world, that is usually the right thing to do.

This is why:

  • /products/shoes?colour=red
  • /products/shoes?colour=blue

…should remain distinct. The query parameter materially changes the content of the page.

But this also means the cache will usually treat these as distinct pages, too—even if all three return byte-for-byte identical HTML.

  • /sale?utm_source=google
  • /sale?utm_source=chatgpt
  • /sale?utm_source=newsletter

At best, that means wasted cache space; at worst, it means unnecessary trips across the network because the browser cannot reuse a perfectly good response that it already has stored.

This is exactly what No-Vary-Search addresses.

What No-Vary-Search Does

No-Vary-Search is a response header that tells the cache how to treat query parameters when matching a URL to an existing cache entry.

In other words, it lets the server say, via a header, these search parameters do not meaningfully change the response, so do not let them fragment the cache.

This is a little reminiscent of the well known Vary header, but aimed squarely at URL search parameters rather than request headers.

That distinction matters. Vary says this response depends on Accept-Language or this response depends on Accept-Encoding, for example. No-Vary-Search says this response does not depend on utm_source or the order of these parameters should not matter, and so on.

If you know that certain parameters are irrelevant to the response body, you can tell the cache to ignore them, effectively allow- or blocklisting them for cache key purposes.

A Simple Example

Imagine a landing page that is heavily used in campaigns, ads, email, and social posts:

  • /offer?utm_source=google
  • /offer?utm_source=chatgpt
  • /offer?utm_source=linkedin

If those all return the same page, you can tell the cache to ignore utm_source:

No-Vary-Search: params=("utm_source")

Now, as far as cache matching is concerned, those URLs may all reuse the same stored response. They will no longer be treated as separate cache entries just because of the different utm_source values. Much more effective reuse of cached content.

That is the crucial thing to understand: No-Vary-Search is not changing the URL, and it is not rewriting requests. It is changing how cache matching treats the differing query string.

No-Vary-Search Syntaxes

The header has a few useful, different forms.

Ignore Specific Parameters

This is the form I suspect most people will use most often:

No-Vary-Search: params=("utm_source" "utm_medium" "utm_campaign" "fbclid" "gclid")

This says: if the only differences between two URLs are those parameters, treat them as the same cache key.

This is ideal for analytics and campaign tagging, where the query string is useful to you but should not change the response for the user. These standard tracking and social parameters could probably be safely applied to most, if not all, sites.

Ignore All Parameters

If your page genuinely does not vary by query string at all, you can be much broader:

A quick update: the params boolean syntax shown below is likely to change in the spec to an empty list, params=(), instead. At the time of writing, this newer syntax is not yet implemented in browsers, so the original params form remains what you’re most likely to encounter in practice. Thanks to Barry Pollard for the heads-up. Keep an eye on the HTTPWG discussion and the Chromium issue for progress.

That is the boolean form of params, and it tells the cache to ignore all search parameters for matching purposes. This works perfectly for my site which has zero back end, and thus cannot possibly vary by query string.

This is obviously powerful, but also the easiest way to shoot yourself in the foot, so only use it if you really mean it.

Ignore Everything Except Certain Parameters

Sometimes the inverse is easier to express. Perhaps most parameters are irrelevant, but a small number genuinely change the response:

No-Vary-Search: params, except=("colour" "size")

This says: ignore all query parameters except colour and size.

That would be a decent fit for a page where:

  • utm_* tags do not matter,
  • client-side sorting/filtering or tracking parameters do not matter (remember, the HTML itself doesn’t change) but,
  • product variants (?colour, ?size) do.

In that world:

  • /products/shoes?utm_source=google&colour=red
  • /products/shoes?utm_source=chatgpt&colour=red
  • /products/shoes?colour=red&sort=price

could share a cache entry, but:

  • /products/shoes?colour=red
  • /products/shoes?colour=blue

should not.

Ignore Parameter Order

Sometimes the parameters themselves matter, but their order does not:

  • /search?q=shoes&sort=price
  • /search?sort=price&q=shoes

These should usually be treated as equivalent. For that, there is key-order:

No-Vary-Search: key-order

That tells the cache not to create separate entries just because the same parameters arrived, only in a different order.

Combining No-Vary-Search Rules

You can combine directives:

No-Vary-Search: key-order, params, except=("colour" "size")

That tells the cache:

  • parameter order does not matter, and
  • all parameters may be ignored except colour and size.

This means the three following URLs could all share a cache entry:

  • /products/shoes?utm_source=google&colour=red
  • /products/shoes?colour=red&sort=price
  • /products/shoes?sort=price&colour=red

That is probably the most expressive form, and in real systems it may prove the most useful. This is phenomenally powerful.

A Small Syntax Gotcha

No-Vary-Search uses Structured Fields syntax, so the parameter lists are space-separated quoted strings:

No-Vary-Search: params=("utm_source" "utm_medium" "gclid")

…not comma-separated values that you may be used to in most other places.

That is a small detail, but one worth being aware of.

A Small Debugging Gotcha

Note that this also creates a slightly unusual debugging scenario. A very common way to force what we assume will be a fresh trip to the server is to throw a random search parameter on the end of the URL. I’m sure we’ve all done something like this before:

  • /?foo
  • /?test
  • /?asdf

Usually, that gives us a different URL and therefore a different cache key. But if the main document is using No-Vary-Search, that assumption may no longer hold. Appending search params may not bypass cache for this document because the cache has explicitly been told those parameters do not matter.

Honestly, I would love DevTools to surface this more clearly. Something like the existing ⚠️ iconography in the Network panel’s title would be really helpful here: not because anything is wrong per se, but because the browser may be doing something surprising unless you know to look for the No-Vary-Search header.

Unknown Params Should Bust the Cache

One of the trickier practical problems with No-Vary-Search is keeping it all in sync. The marketing team may start using a new utm_* parameter, or an ecommerce team may ship a new filter or facet, and unless that change is reflected in the No-Vary-Search header, caching behaviour may well differ from the ideal scenario.

For that reason, the sensible and defensive default is to always let new and unknown parameters vary the cache key. In other words, if you do not yet know whether a parameter is meaningful, treat it as semantic until proven otherwise.

Imagine marketing starts adding new params, e.g.:

  • ?utm_campaign_variant=summer-a
  • ?utm_campaign_variant=summer-b

If those parameters are absent from No-Vary-Search, then yes, you will miss out on some caching opportunities, but that is no slower than the situation we’ve lived with all along. The worst case scenario is just the status quo: separate cache entries for URLs that are materially the same.

On the flip side, imagine the ecommerce team introduces:

  • ?material=leather
  • ?material=canvas

If those parameters are absent from No-Vary-Search, that is by far the safer outcome. We’d much rather let them produce separate cache entries than accidentally fold distinct pages into one.

This is the same basic principle I recently wrote about in When All You Can Do Is All or Nothing, Do Nothing: when the system lacks the context to be precise, the safer fallback is usually the less clever one. In this case, that means allowing unknown parameters to bust cache until someone has explicitly decided they are safe to ignore.

Again, this is not wasteful—it’s just the same behaviour we’ve always had.

Need Some Help?

I help companies find and fix site-speed issues. Performance audits, training, consultancy, and more.

Use It Carefully

This header is only as good as the assumptions behind it: if two URLs really do return meaningfully different content, then they need different cache entries. I’d rather be served the correct page a little more slowly than the wrong page quickly.

This means No-Vary-Search is best suited to parameters that are:

  • purely analytical;
  • purely presentational on the client side, or;
  • otherwise irrelevant to the server-rendered response.

If a parameter affects the HTML, do not ignore it.

It’s also worth noting that, at the time of writing, this is still an experimental feature and support is not yet universal, so I would treat it as a progressive enhancement rather than a foundational part of your caching strategy. That’s exactly what I’ve done with my site for now.

A Nice Fit for Messy Real-World URLs

What I like about No-Vary-Search is that it acknowledges how the web actually works. URLs pick up baggage: marketing tags get appended, tracking parameters are added, client-side state makes its way into the address bar. Two URLs that are materially the same page often arrive looking totally different.

Historically, caches had to treat those as entirely separate keys, but No-Vary-Search gives us a way to be a little more deliberate. If the response is the same, we can say so. And if only certain parameters matter, we can say that, too.

For teams who care about getting more out of the HTTP cache, that is a very welcome addition!


Frequently Asked Questions

What is No-Vary-Search?

No-Vary-Search is an HTTP response header that tells caches which URL search parameters can be ignored when matching requests to cached responses.

What problem does No-Vary-Search solve?

It reduces cache fragmentation caused by irrelevant query parameters such as UTM tags, so materially identical URLs can reuse the same cached response.

When should I use No-Vary-Search?

Use it when some query parameters do not meaningfully change the response body, such as analytics, campaign, or other tracking parameters.

When should I not use No-Vary-Search?

Do not use it for parameters that change the HTML or otherwise alter the response in a meaningful way, such as product variants or content filters rendered on the server.

Can No-Vary-Search ignore all query parameters?

Yes. Today, the `params` form can tell caches to ignore all search parameters, but the spec is moving toward `params=()` instead. In either case, it should only be used when the response truly does not vary by query string.

Does No-Vary-Search affect debugging?

Yes. Appending a throwaway query string to try to bypass cache may no longer work if the document uses No-Vary-Search and the cache has been told those parameters do not matter.