Cache Matching Semantics in Service Workers
Cache matching determines whether a stored response corresponds to a request and which request properties are considered during that lookup. In the Cache API, URL, HTTP method, and the stored response's `Vary` header can all affect matching, with options available to relax some of these checks.
`cache.match(request, options)` searches a particular `Cache` object for a matching stored request. It does not search every cache in the origin; choosing the cache is a separate step, commonly through `caches.open()`.
By default, the request URL is compared including its query string, so `/app.js?v=1` and `/app.js?v=2` are different cache keys. Setting `ignoreSearch: true` makes the URL comparison disregard the query component.
Request methods also matter. A normal cache match is restricted to `GET` and `HEAD`; `ignoreMethod: true` allows method matching to be bypassed. This option does not mean that arbitrary HTTP semantics should automatically be treated as interchangeable in an application.
The stored response's `Vary` header can impose additional matching requirements. Unless `ignoreVary` is true, the relevant request header values are compared according to `Vary`, so two requests for the same URL can still map differently when their negotiated representation differs.
If no entry satisfies the matching rules, `cache.match()` resolves to `undefined`. These rules are distinct from cache storage itself: `cache.put()` determines what gets stored, while `match()` determines whether a later request can retrieve that entry.
Key points
- `cache.match()` searches one `Cache` object for a matching stored request.
- Query strings participate in matching unless `ignoreSearch` is enabled.
- Only `GET` and `HEAD` requests match by default; `ignoreMethod` can relax that check.
- `Vary` can make matching depend on request-header values unless `ignoreVary` is enabled.
- A failed lookup resolves to `undefined`.
Interview questions
How would you make `/data?page=1` and `/data?page=2` match the same cached entry?
Use `cache.match()` with `{ ignoreSearch: true }`. This causes the query portion of the URL to be disregarded during the lookup. It does not rewrite or remove the query string from the stored request. This should only be used when those query parameters are irrelevant to the representation being served.
What does `cache.match()` actually compare when looking for a cached response?
It compares the supplied request against stored requests in the selected `Cache`. The URL is normally compared with its query string, and the request method is considered. The stored response's `Vary` header can also affect whether the entry matches. Matching options can relax some of these rules.
Why can two requests for the same URL fail to match when the response has a `Vary` header?
`Vary` tells caches that the representation can depend on particular request headers. During matching, the relevant headers from the incoming request are compared with those associated with the cached response's request. Therefore, the URL alone is not necessarily sufficient for a match. `ignoreVary: true` can bypass this part of the matching process when appropriate.
What is the difference between `Cache.match()` and `CacheStorage.match()`?
`Cache.match()` operates on a particular `Cache` instance, such as one returned by `caches.open('assets')`. `CacheStorage.match()` operates across the cache storage associated with the origin and can search the available named caches. The distinction matters when an application separates entries into multiple caches. A developer should choose the API whose search scope matches the intended cache architecture.
When might using `ignoreSearch` or `ignoreVary` cause a correctness problem?
These options deliberately discard distinctions that may be meaningful to the response representation. `ignoreSearch` can return the wrong content when query parameters select different resources or variants. `ignoreVary` can return a representation negotiated for different request headers, such as an incompatible content encoding or language. They should therefore be used only when the application has established that the ignored dimensions cannot change the response that should be served.