Service Worker Caching: The Free Performance Boost 90% of Sites Are Missing

Most websites load everything fresh on every visit. Every CSS file, every JavaScript bundle, every image. From the network. Every. Single. Time. Even if the user visited five minutes ago and nothing c

Glowing gear icon inside a shield shape surrounded by cache storage cylinders and network request arrows on dark navy background

Most websites load everything fresh on every visit. Every CSS file, every JavaScript bundle, every image. From the network. Every. Single. Time. Even if the user visited five minutes ago and nothing changed.

Service workers fix this. They sit between your page and the network and serve files from a local cache when possible. Repeat visitors get near-instant load times. First-time visitors get the same experience they always did. And you did not have to pay for a CDN upgrade to get it.

This is not a PWA guide. You do not need to make an "app." You do not need install prompts or offline modes. You just need a background script that caches your static files. That is it.

What a Service Worker Actually Does

A service worker is a JavaScript file that runs in a separate thread from your main page. It cannot touch the DOM. It cannot access your page's variables. It just intercepts network requests and decides what to do with them.

Here is the simplest possible service worker:

// sw.js
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      return cached || fetch(event.request);
    })
  );
});

That 6-line service worker catches every network request, checks the cache, and serves the cached version if it exists. If not, it fetches from the network. That is the core concept. Everything else is just variations on this pattern.

The Performance Impact: Real Numbers

For a typical website with static assets (CSS, JS, fonts, images) cached by a service worker:

That is a dramatic improvement for repeat visitors. And a large portion of your traffic is repeat visitors. E-commerce sites often see 40-60% repeat visitor rates. News sites even higher. Every one of those users benefits from service worker caching.

Cache Strategies: Which One Goes Where

Cache First (For Static Assets)

Check cache first. If cached, serve immediately. Only go to network if not cached. This is the strategy for files that rarely change: your CSS bundles, JavaScript files, fonts, and images.

// Cache First: perfect for CSS, JS, fonts, images
async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;

  const response = await fetch(request);
  const cache = await caches.open('assets-v1');
  cache.put(request, response.clone());
  return response;
}

Network First (For HTML Pages)

Try network first. If network fails (offline or slow), fall back to cache. Use this for your HTML pages, API endpoints that return dynamic data, and anything that changes frequently.

// Network First: for HTML and dynamic content
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('pages-v1');
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}

Stale While Revalidate (For Blog Content)

Serve from cache immediately (fast response), then fetch fresh version in the background and update the cache. Perfect for content that changes occasionally but does not need to be up-to-the-second fresh.

// Stale While Revalidate: for blog posts, documentation
async function staleWhileRevalidate(request) {
  const cache = await caches.open('content-v1');
  const cached = await cache.match(request);

  // Start fetching fresh version in background
  const fetchPromise = fetch(request).then(response => {
    cache.put(request, response.clone());
    return response;
  });

  // Serve cached immediately, or wait for network
  return cached || fetchPromise;
}

Why Workbox Makes This Practical

Writing service workers by hand works but quickly gets complicated. Cache versioning, precaching, handling fetch errors, background sync — these are all things you need to handle correctly or your users will see stale content or broken pages.

Workbox (from Google) handles all of this. Here is a complete Workbox setup that covers most sites:

// sw.js with Workbox
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { precacheAndRoute } from 'workbox-precaching';

// Precache: files built at compile time (versioned filenames)
precacheAndRoute(self.__WB_MANIFEST);

// Cache First: static assets
registerRoute(
  ({ request }) => request.destination === 'style'
    || request.destination === 'script'
    || request.destination === 'font',
  new CacheFirst({
    cacheName: 'static-assets',
    plugins: [{
      cacheWillUpdate: async ({ response }) =>
        response.status === 200 ? response : null
    }]
  })
);

// Stale While Revalidate: images
registerRoute(
  ({ request }) => request.destination === 'image',
  new StaleWhileRevalidate({
    cacheName: 'images',
  })
);

// Network First: HTML pages
registerRoute(
  ({ request }) => request.mode === 'navigate',
  new NetworkFirst({
    cacheName: 'pages',
    networkTimeoutSeconds: 3,  // Fall back to cache after 3s
  })
);

The Cache Invalidation Problem (And How to Solve It)

The hardest part of service worker caching is not caching things. It is un-caching them when they change. If you cache your app.js and then ship a bug fix, users with the old version cached will keep seeing the broken version until their browser updates the service worker.

The solution is file versioning at the build step. Instead of app.js, your build tool generates app.abc123de.js where the hash changes when the file content changes. Cache these files indefinitely. When you ship a new version, it has a new hash, so it gets cached fresh. Old files eventually get evicted.

Workbox's precache manifest handles this automatically. It generates a list of all your versioned files with their hashes, and the service worker manages the cache lifecycle for each file. When you build and deploy, Workbox updates the manifest, the service worker updates, and users get fresh files automatically.

Common Service Worker Mistakes to Avoid

How to Register Your Service Worker

// Register in your main JavaScript file
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js');
      console.log('Service Worker registered:', registration.scope);

      // Check for updates on each page load
      registration.addEventListener('updatefound', () => {
        const newWorker = registration.installing;
        newWorker.addEventListener('statechange', () => {
          if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
            // New version available - show update notification to user
            showUpdateNotification();
          }
        });
      });
    } catch (error) {
      console.error('Service Worker registration failed:', error);
    }
  });
}

FAQ

Do service workers work on Safari/iOS?

Yes. Safari has supported service workers since version 11.1, which is well over 95% of active Safari users in 2026. iOS Safari has the same support. The only limitation is that service workers on iOS clear cache storage after about a week of the user not visiting. This is a Safari-specific behavior to conserve storage space.

Will a service worker break my site if implemented wrong?

It can. The most common problem is accidentally caching the HTML page with Cache First. If users get a cached version of your HTML, they will not see content updates. Test thoroughly before deploying. Also, if you need to remove a broken service worker, you can do it from Chrome DevTools under Application, Service Workers, Unregister.

Do I need a service worker if I already have Cloudflare?

They do different things. Cloudflare caches at the CDN edge, which helps first-time visitors by serving files from a server close to them. Service workers cache in the browser, which helps repeat visitors by serving files without any network request at all. CDN caching is about geographic proximity. Service worker caching is about eliminating the network entirely for cached resources. You can and should use both.

Still Getting Red Scores?

Run a free audit and get a punch-list of exactly what to fix. No account needed.

Run Free Audit →

Still Getting Red Scores?

Run your site through VitalsFixer. Free audit in 30 seconds, no account needed.

Analyze My Site Free →

Want an Expert to Handle It?

Real engineers, 48-hour turnaround, money back if scores don't improve.

View Expert Fix →