Skip to content

Support No-Vary-Search header in Cache API #1798

@yoshisatoyanagisawa

Description

@yoshisatoyanagisawa

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions