# The Cache API

> How to open, populate, query, and delete caches using the Cache Storage API — the durable key/value store for Request/Response pairs available to service workers and pages.

**In one line:** The Cache API (`caches` global) is a persistent, origin-scoped key/value store for `Request`/`Response` pairs. Service workers use it to store resources during `install` and serve them offline; unlike the browser HTTP cache, the Cache API is fully programmatic — nothing is stored or evicted without your code doing it.

## The `caches` global

`caches` is a `CacheStorage` instance available in service workers and in pages (since Chrome 43 / Firefox 41). It manages a named collection of `Cache` objects.

```js
// Available in both service workers and pages
const cache = await caches.open('my-cache-v1');
```

## Opening a cache

```js
const cache = await caches.open('app-shell-v3');
```

`caches.open()` creates the cache if it does not exist. Cache names are arbitrary strings; use versioned names (`app-shell-v3`) so you can delete old caches in `activate`.

## Adding resources

### `cache.add(request)` — fetch and store one resource

```js
await cache.add('/offline.html'); // fetches, then stores
```

### `cache.addAll(requests)` — fetch and store multiple resources atomically

```js
await cache.addAll([
  '/',
  '/app.js',
  '/app.css',
  '/offline.html',
]);
```

`addAll` is atomic: if any request fails, nothing is stored. Use it in `install` inside `event.waitUntil()` so a failed pre-cache aborts the install.

### `cache.put(request, response)` — store a response you already have

```js
const response = await fetch(event.request);
await cache.put(event.request, response.clone());
```

`put` does not fetch — it stores the `Response` you pass directly. You must `clone()` the response before storing if you also intend to use it (responses are single-read streams).

## Querying

### `cache.match(request, options?)` — look up one entry

```js
const cached = await cache.match('/app.js');
if (cached) return cached;
```

### `caches.match(request)` — query all caches in insertion order

```js
const cached = await caches.match(event.request);
```

`caches.match()` searches every open cache and returns the first match. Useful when you don't know which cache holds a resource.

### Match options

```js
await cache.match(request, {
  ignoreSearch: true,   // ignore query string (?v=1)
  ignoreMethod: true,   // match POST as if GET
  ignoreVary: true,     // ignore Vary header
});
```

## Deleting entries and caches

```js
// Remove one entry from a cache
await cache.delete('/old-resource.js');

// Delete an entire named cache
await caches.delete('app-shell-v2');

// List all cache names
const names = await caches.keys();
```

The standard pattern is to delete old caches in the `activate` event after a new version installs:

```js
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((names) =>
      Promise.all(
        names
          .filter((n) => n !== 'app-shell-v3') // keep only the current cache
          .map((n) => caches.delete(n))
      )
    )
  );
});
```

## Storage limits

Cache Storage is subject to the origin's storage quota (shared with IndexedDB, OPFS, and other storage APIs). Browsers evict data when the device is under storage pressure, starting with origins that have not been used recently — unless the origin has requested persistent storage. See [Persistence, quotas, and eviction](/reference/storage/persistence/) for details.

## Caching opaque responses

A cross-origin response fetched with `no-cors` is *opaque* (`status: 0`, body unreadable). You can cache an opaque response, but:

- You cannot inspect its status code to verify it succeeded.
- It may be counted as a large entry (browsers pad opaque responses in quota accounting to prevent timing attacks).
- Stale opaque responses are indistinguishable from failed ones.

Prefer same-origin or CORS-enabled resources in caches. If you must cache an opaque response, always pair it with a revalidation strategy.

## Practical checklist

- [ ] Version your cache names (`shell-v3`, not `shell`) and delete previous versions in `activate`.
- [ ] Use `cache.addAll()` in `install` to pre-cache the app shell atomically.
- [ ] Always `clone()` a response before calling `cache.put()` if you also return it to the caller.
- [ ] Prefer `cache.match()` over `caches.match()` when you know which cache to search — it's faster.
- [ ] Monitor storage usage in DevTools → Application → Storage to catch quota creep.

## Cross-references

- [Caching strategies](/reference/service-worker/caching-strategies/) — the patterns that use the Cache API
- [The Fetch event and routing](/reference/service-worker/fetch-event/) — where `caches.match()` is called at request time
- [Persistence, quotas, and eviction](/reference/storage/persistence/) — storage quotas and eviction policy