Beyond the HTTP cache: service workers and the Cache Storage API

The browser's HTTP cache is a solid first line of defense, but it has a blind spot: it only works well for versioned URLs that have been visited before. To get reliable performance for everything else, you need two newer tools that are designed to work together: service workers and the Cache Storage API.

Service workers: intercepting requests

A service worker is a script file that you deploy alongside your web app. The browser runs it in the background, where it can intercept outgoing network requests from your pages. What happens next is entirely up to your code.

For some requests, the right move is to let them flow to the network as if the service worker weren't there. For others, you can respond from an in-browser cache that is far more flexible than the HTTP cache, avoiding the network entirely.

The Cache Storage API

Jeff Posnick

The Cache Storage API gives you direct, programmatic control over the contents of the cache. Instead of relying on HTTP headers and browser heuristics to decide what gets stored, your JavaScript decides exactly what goes in and what comes out. It is designed to be called from inside a service worker.

How this cache relates to the HTTP cache

Adding another cache layer raises a fair question: do your HTTP headers still matter? Yes, they do. The HTTP cache and the Cache Storage API are not in competition. When you populate the Cache Storage API, the browser checks the HTTP cache for an existing entry first and reuses it if it's there, which saves a network request for versioned assets. But poor HTTP cache configuration can bite you here too: if you give an unversioned URL a long cache lifetime, the stale response can end up in your Cache Storage API as well. It's worth making sure your HTTP cache behavior is correct before leaning on the new API.

If you get headers right, the Cache Storage API opens up patterns that are difficult or impossible with the HTTP cache alone, including:

  • Stale-while-revalidate, where cached content is displayed during a background refresh.
  • Limiting the number of cached assets and implementing a custom expiration policy.
  • Comparing cached and fresh network responses to detect changes and prompt the user to update content when needed.

API fundamentals

When you start working with the Cache Storage API, keep these design details in mind:

  • Request objects are the unique keys. For convenience, a URL string like 'https://example.com/index.html' is accepted as a key as well.
  • Response objects are the values stored in these caches.
  • The Cache-Control header is effectively ignored when data is stored in the Cache Storage API. There are no built-in expiration or freshness checks: entries persist until your code explicitly removes them.
  • Unlike legacy synchronous APIs such as LocalStorage, all Cache Storage operations are asynchronous.

A note on asynchronous programming

Both service workers and the Cache Storage API depend on asynchronous programming patterns, particularly promises. Before diving into implementation, make sure you're comfortable working with promises and the async/await syntax.

Production readiness comes later

These two APIs are fundamental building blocks, but they're low-level by design, and there are edge cases to handle. A number of libraries exist to smooth over those rough patches and give you a production-ready service worker without all the manual wiring. Tools like Workbox, covered in a separate guide, offer a faster path to a full implementation.