w3c / w3c/ServiceWorker

Support No-Vary-Search header in Cache API

Open
#1,798 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Bikeshed
Stars
3.6k
Forks
324
Avg merge
14d 22h
Merged PRs (30d)
1

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Cache API entry points named in the issue: Cache.put(), Cache.add(), Cache.match(), and CacheStorage.match(), then read the linked No-Vary-Search reference and the ServiceWorker specification. Resolve whether support is implicit or opt-in, define how stored rules affect matching, and document the behavior for existing entries and each proposed operation.

Written by the indexing model from the issue text.

Assessment

Domain
api, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.