[w3c/ServiceWorker] Proposal: Support No-Vary-Search header in Cache API (Issue #1798)

yoshisatoyanagisawa created an issue (w3c/ServiceWorker#1798)

**Problem Description**
Currently, the Cache API (defined in the ServiceWorker specification) provides mechanisms for matching requests, including `ignoreSearch` in the `match()` options. While `ignoreSearch: true` is useful for ignoring all query parameters, it lacks the granularity needed for modern web applications.

The `No-Vary-Search` HTTP header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/No-Vary-Search) allows servers to specify which query parameters are relevant for caching, distinguishing between parameters that modify the content (e.g., `article_id`) and those that do not (e.g., `utm_source`).

The current Cache API may not understand the `No-Vary-Search` header. This means developers cannot efficiently cache responses that rely on this mechanism. They are forced to either:

- Use `ignoreSearch: true`, which is too broad and may serve the wrong content if some query parameters are significant.
- Not use `ignoreSearch`, which leads to cache misses for requests that only differ by insignificant query parameters (like analytics tags).

**Proposal**
We propose extending the Cache API (`Cache.put()`, `Cache.add()`, and `CacheStorage.match()` / `Cache.match()`) to understand and respect the `No-Vary-Search` header.

When a response is stored using `put()` or `add()`, the Cache API should (potentially) store the `No-Vary-Search` rules associated with that response. When `match()` is called, the API should use these stored rules to determine if a cached response matches the request, even if the query parameters do not match exactly.

The key question is how this support should be implemented. We have identified several potential approaches:

**Discussion of Potential Approaches**
We see a few ways this could be integrated, primarily concerning whether the behavior is implicit (automatic) or explicit (opt-in by the developer).

**Option 1: Explicit `add()`, Implicit `match()`**
In this model, the developer must explicitly state that No-Vary-Search should be considered when storing the response.

`Cache.put(request, response, options)`: We could add a new option, e.g., `noVarySearch: true.`
```
cache.put(request, response, { noVarySearch: true });
```
`CacheStorage.match(request, options)`: The `match()` operation would automatically respect the No-Vary-Search rules for any cache entries that were stored with this flag. The match() call itself would not need a special flag.

**Pros:**
Gives developers control over which entries should use this logic.

**Cons:**
The `match()` behavior becomes "magical" or implicit, depending on how the entry was added, which might be confusing.

**Option 2: Fully Implicit (Automatic)**
This model mirrors how HTTP caches (like browser caches) are expected to work.

`Cache.put(request, response)`: The API automatically inspects the response.headers for a `No-Vary-Search` header. If present, it stores the response and its `No-Vary-Search` rules.

`CacheStorage.match(request)`: The match() operation automatically uses the stored No-Vary-Search rules if they exist for a cache entry.

**Pros:**
Most "standards-compliant" and requires no developer effort if the server sends the correct headers.

**Cons:**
This changes the fundamental behavior of the Cache API, which has traditionally been a simple key/value (Request/Response) store. Existing caches might not be compatible. Does this apply to all existing cache entries retroactively (if possible) or only new ones?

**Option 3: Fully Explicit (Opt-in on both ends)**
This model requires the developer to opt-in at both storage time and lookup time, perhaps treating `No-Vary-Search`-aware entries as a distinct "type" of cache.

`Cache.put(request, response, options)`: Requires an option, e.g., `noVarySearch: true`.

`CacheStorage.match(request, options)`: Also requires an option, e.g., `noVarySearch: true`.

A `match()` call without the option would not match an entry stored with the option, and vice versa. This effectively partitions the cache.

```
// Store the entry
await cache.put(request, response, { noVarySearch: true });

// This will NOT find it (default behavior)
const match1 = await cache.match(requestWithAnalyticsParams); 

// This WILL find it
const match2 = await cache.match(requestWithAnalyticsParams, { noVarySearch: true });
```

**Pros:**
Most explicit and least "magical." It avoids breaking backward compatibility.

**Cons:**
Most verbose for the developer. It creates a "split brain" in the cache, where the developer must always remember which "mode" their entries are stored in.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/w3c/ServiceWorker/issues/1798
You are receiving this because you are subscribed to this thread.

Message ID: <w3c/ServiceWorker/issues/1798@github.com>

Received on Tuesday, 21 October 2025 05:00:41 UTC