# Cache API

> The CacheStorage and Cache interfaces behind the caches global, their members, the TypeError and QuotaExceededError conditions on add(), addAll(), and put(), and the versioned-cache pattern for install and activate.

The Cache API is an origin-scoped, persistent store of `Request`/`Response` pairs reached through the `caches` global (a `CacheStorage`) in both service workers and pages. Nothing is written or evicted except by your code or by storage-pressure eviction of the whole origin. It is available in Chrome 43, Edge 16, Firefox 41, and Safari 11.1 in secure contexts only (BCD `api.CacheStorage`).

## Syntax

```js
// The caches global
const cache = await caches.open(cacheName);
const hit = await caches.match(request, options);
const exists = await caches.has(cacheName);
const names = await caches.keys();
const removed = await caches.delete(cacheName);

// Cache
await cache.add(request);
await cache.addAll([request1, request2]);
await cache.put(request, response);
const response = await cache.match(request, options);
const responses = await cache.matchAll(request, options);
const requests = await cache.keys(request, options);
const deleted = await cache.delete(request, options);
```

Every method returns a promise. A `request` argument may be a `Request` or a URL string, which is resolved against the worker's or page's location. `caches.open()` creates the cache when it does not exist.

## Members

`CacheStorage` manages named caches; `Cache` holds the entries of one of them.

| Member | Returns | Behaviour |
|---|---|---|
| `caches.open(name)` | `Promise<Cache>` | Opens or creates the named cache. Names are arbitrary strings, which is what makes versioning (`shell-v3`) possible. |
| `caches.match(request, options)` | `Promise<Response \| undefined>` | Searches every cache of the origin in creation order and resolves with the first hit. `options.cacheName` restricts the search to one cache. |
| `caches.has(name)`, `caches.keys()`, `caches.delete(name)` | `Promise<boolean>`, `Promise<string[]>`, `Promise<boolean>` | Inventory and removal of whole caches. `delete()` resolves `false` when the name did not exist. |
| `cache.add(request)` | `Promise<void>` | Fetches the request and stores the response. Shorthand for `fetch()` followed by `put()`. |
| `cache.addAll(requests)` | `Promise<void>` | Fetches all requests and stores them atomically: when one fails, none is stored. |
| `cache.put(request, response)` | `Promise<void>` | Stores a response you already hold, without fetching. The body stream is consumed, so `clone()` first when you also return it. |
| `cache.match(request, options)` | `Promise<Response \| undefined>` | Looks up one entry in this cache. |
| `cache.matchAll(request, options)` | `Promise<Response[]>` | All entries matching the request, or every entry when `request` is omitted. |
| `cache.keys(request, options)` | `Promise<Request[]>` | The stored `Request` objects, useful for pruning by URL. |
| `cache.delete(request, options)` | `Promise<boolean>` | Removes matching entries; resolves `true` when at least one was removed. |

`options` is a `CacheQueryOptions` dictionary: `ignoreSearch` drops the query string from the comparison, `ignoreMethod` lets a non-GET request match a stored GET entry, and `ignoreVary` skips `Vary` header matching. Entries are keyed by URL and method, and a stored response's `Vary` header is honoured by default, so a request with a different `Accept-Language` can miss an entry that the DevTools view shows as present.

## Exceptions

Rejections carry a `DOMException` or a `TypeError`.

- `TypeError`: the request method is not `GET` (`add`, `addAll`, `put`), the request scheme is not `http:` or `https:`, the response has status `206`, or its `Vary` header contains `*`. Chromium's messages include `Partial response (status code 206) is unsupported` and `Vary header contains *`.
- `TypeError` from `add()` and `addAll()`: a fetched response is not `ok` (status outside 200 to 299) or the fetch fails. Chromium reports `Request failed`, so one 404 in a precache list aborts the whole `addAll()` and, when it runs inside `install`'s `waitUntil()`, the install.
- `QuotaExceededError`: the origin's storage quota is exhausted when `put()`, `add()`, or `addAll()` writes. Chromium pads opaque (`no-cors`, status `0`) responses to about 7 MB each in quota accounting, so a handful of third-party assets can trigger it on a near-full device.
- `SecurityError`: `caches` is accessed from an insecure context. On an `http:` origin other than `localhost`, `window.caches` is `undefined` and `self.caches` is unavailable in workers.

:::observed
In Chrome (English UI), a precache list that includes one URL returning 404 fails the install with the worker-console message `Uncaught (in promise) TypeError: Failed to execute 'addAll' on 'Cache': Request failed`, and DevTools › Application › Service workers then shows the worker as `#<id> is redundant`; the string `Request failed` is the one in Chromium's `cache.cc`. DevTools › Application › Storage › Cache storage lists each cache as `<name> - <origin>` and shows per-entry **Response-Type** (`basic`, `cors`, `opaque`) and **Content-Length** columns, which is the quickest way to spot opaque entries inflating quota.
:::

## Examples

The two examples below are the install and activate halves of the versioned-cache pattern, followed by a detection example for pages that must run where `caches` is absent.

### Precaching the app shell atomically in `install`

Use `addAll()` inside `event.waitUntil()` so that a single failed asset keeps the broken worker out of the active slot.

```js
const SHELL = 'shell-v3';
const ASSETS = ['/', '/app.js', '/app.css', '/offline.html'];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(SHELL).then((cache) => cache.addAll(ASSETS))
  );
});
```

Because `addAll()` is atomic, `shell-v3` is either complete or absent; a 404 on `/app.css` rejects the promise, the install fails, and the previously active worker keeps serving.

### Deleting superseded caches in `activate`

Old caches are not removed for you. Delete every name that is not the current one once the new worker activates.

```js
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((names) =>
      Promise.all(names.filter((n) => n !== SHELL).map((n) => caches.delete(n)))
    )
  );
});
```

Run this in `activate`, not `install`: during `install` the old worker may still be serving pages from the cache you would be deleting.

### Detecting support and running online-only without it

`caches` is missing on insecure origins and in browsers without the API. The fallback below skips service worker registration and tells the app not to advertise offline support.

```js
export function initOffline() {
  if ('caches' in window && 'serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js');
    return { offline: true };
  }
  // No Cache Storage: run online-only and hide the "Available offline" badge.
  return { offline: false };
}
```

Pages that call `caches.open()` directly should guard the same way; a bare call on an `http:` origin throws before any promise is created.

## See also

- [Service Workers specification: Cache interface](https://w3c.github.io/ServiceWorker/#cache-interface) (w3c.github.io)
- [View Cache data in Chrome DevTools](https://developer.chrome.com/docs/devtools/storage/cache) (developer.chrome.com)
- [CacheStorage](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage) (developer.mozilla.org)
- [Caching strategies for service workers](/reference/service-worker/caching-strategies/)
- [FetchEvent and request routing](/reference/service-worker/fetch-event/)
- [Cache Storage](/reference/storage/cache-storage/)
- [Storage persistence, quotas, and eviction](/reference/storage/persistence/)