Getting Started with Service Workers

Ritesh Kumar
Ritesh Kumar

There was a time when people only related the use of push notifications to mobile applications. Luckily, that time has passed. Now there are Service Workers which can help us implement push notifications in desktop applications, and open up websites even when you’re offline. A Service Worker is a script that runs in the background. It doesn’t need a web page or user interaction in order to work. This means that it will run even when your website is not open, even if it can’t access the DOM directly (the DOM can use the postMessage API to communicate with the Service Worker, though). Currently, they include features like push notifications and geofencing. It can also intercept and handle network requests, that is the feature that we’re going to use in this tutorial. To those of you who are curious about browsers support, I suggest to take a look here. As you’ll see, its implementation is still at an early stage. In order to demonstrate how the network intercepting feature of Service Workers works, we’ll make a static website which runs even when the user is offline. You can find the whole demo of the website here. Service Workers give you the control of a web page where you can programmatically select the components you want to cache. Keep in mind that it’ll run offline only on second or subsequent visits. The reason behind this behavior will be explained later in this tutorial. final result One common problem that Service Workers have is that they only work in “secure origins” (HTTPS sites, basically) in line with a policy which prefers secure origins for powerful new features. However, even localhost is considered a secure origin, so developing on it is an easy way to avoid this error. If you prefer, you could also use GitHub Pages (as I did) since they’re served over HTTPs.

Getting Started

The first thing that we need to do is to register the Service Worker. This will work only if the browser supports it. This means that all the following code snippets you’ll find throughout this tutorial will be valid only if navigator.serviceWorker exists.
//make sure that Service Workers are supported.
if (navigator.serviceWorker) {
    navigator.serviceWorker.register('./service-worker.js', {scope: './about'})
        .then(function (registration) {
        .catch(function (e) {
} else {
    console.log('Service Worker is not supported in this browser.');
In the above code ./service-worker.js is the path of the Service Worker. The scope is the path on which the Service Worker will act. In this example the Service Worker will control the page having the path /about/
. The scope is optional and has ./ by default. The register method returns a promise. We can call the register
method as many times as we want. When this is done, the browser will automatically figure out if it has been already registered and will register it only if it hadn’t been registered earlier. You can view all the registered Service Workers by going to chrome://serviceworker-internals.


In a Service Worker we can register event listeners for various events triggered by the browser. The install event is triggered when the browser sees the Service Worker for the first time. When you open the Chrome’s developers tools you won’t be able to see the log because the Service Worker runs in a completely different thread. We’ll discuss more on debugging in the later part of the tutorial.
self.addEventListener('install', function(event){

self.addEventListener('activate', function(event){
At this point we’ll intercept the requests made to the server. To do so, we listen to the 'fetch' event using the self.addEventListener
method, which returns the event object in the callback. We get the request URL as the value of event.request.url.
self.addEventListener('fetch', function(event){
  // return something for each interception
If you want to import any external script in the Service Worker, you can do it using importScripts() . In this example we’ll be using the cache-polyfill
since the support for cache is limited.

var CACHE_VERSION = 'app-v1';

self.addEventListener('install', function (event) {
            .then(function (cache) {
                console.log('Opened cache');
                return cache.addAll(CACHE_FILES);
In our install event listener, we use the waitUntil() method from the provided event
object to tell the browser with a promise when the installation process in our Service Worker is finished. The provided promise is the return value of the method that opens the cache with name ‘app-v1’. Once the cache opens properly we add our assets to it. The install method only finishes once the assets are saved. If there is an error in saving any one of the assets, then the Service Worker won’t be registered successfully. This means that we should ensure that we only cache important files as a higher number of files will increase the probability of failure. You should only cache the components that improve the perceived load time of the web page. When the installation step is finished, the Service Worker activates. This is where the Service Worker takes control of the page. Now the requests are being intercepted but we need to figure out what we are going to do once this happens. There may be cases when the Service Worker can’t read the data from the cache or the request doesn’t match the assets’ request URL that is saved in the cache.
Here’s what we’re going to do once we intercept the request:
  1. First we open the cache and match the request with the ones present in the cache. If they match, we return the data from the cache. If the request doesn’t match, we redirect the request to the server.
  2. When the data is received successfully from the server, we return that data.
  3. Then we open the cache and save that data here using cache.put() so that it can be accessed directly from the cache in following attempts.
self.addEventListener('fetch', function (event) {
                return res;

function requestBackend(event){
    var url = event.request.clone();
    return fetch(url).then(function(res){
        //if not a valid response send the error
        if(!res || res.status !== 200 || res.type !== 'basic'){
            return res;

        var response = res.clone();{
            cache.put(event.request, response);

        return res;
Now, let’s analyze a scenario in which we would need to update the cache, which is common since it’s necessary every time the files are changed. Once your files have been changed, you need an update in the cache. Here is how we have to proceed:
  1. Update CACHE_VERSION because if the browser detects any change in the Service Worker, it will redownload it. The install event in the new service worker will be fired but the new Service Worker will enter in the ‘waiting’ stage as the page will still be controlled by the old Service Worker.
  2. When all the instances of your website are closed, the new Service Worker will take control (instead of the older one).
  3. At this point the install event will be fired and here we’ll need to do some cache management.
We’ll find all the keys different from the current version and then we’ll clean them by using the function below.
self.addEventListener('activate', function (event) {
            return Promise.all(, i){
                if(key !== CACHE_VERSION){
                    return caches.delete(keys[i]);
The ServiceWorkers will be installed when you visit the website for the first time. Don’t expect them to take control of the page on the first visit. They will just get registered and installed. The request will go to the server and the assets will be fetched from there. Moreover, in the meantime they’ll be saved in the cache. In later visits, the Service Worker will intercept the requests and return the assets from the cache. In order to get a better feel of all this, open the Networks tab in the developer tools. If you re-open the page later, you’ll see that all the cached assets are being fetched from the Service Worker. Network One thing to keep in mind is that the browser controls the lifetime of a Service Worker. The time it runs for after the installation is not fixed.


Service Worker’s debugging is a bit tricky for a beginner. You have to enable it as it’s still an experiment. To do it, follow these steps:
  1. Go to chrome://flags and enable the option ‘Enable DevTools Experiments’.
  2. Open DevTools, then go to Settings > Experiments and hit Shift 6 times.
  3. Check “Service Workers in Resources panel” and restart DevTools
Now you have this experiment enabled and you can find the option in the Resources tab of DevTools. Debugging If you want to manually unregister a Service Worker, go to chrome://serviceworker-internals/ and click on the corresponding “Unregister” button. Some more insights into the debugging process can be found here.


In this article we have created a website which demonstrates the use of Service Workers to create offline web applications. We also discussed few concepts regarding Service Workers and how to debug them.
I really hope you enjoyed the tutorial.
If you want to play with the source code, you can find it here.

Frequently Asked Questions about Service Workers

What are the key differences between service workers and web workers?

Service workers and web workers are both types of JavaScript workers, but they serve different purposes. Web workers are designed to offload intensive computation tasks to a separate thread, preventing the main thread from becoming blocked and ensuring a smooth user experience. On the other hand, service workers act as a network proxy, intercepting network requests and controlling how they are handled. They are primarily used for features like offline support, background sync, and push notifications.

How can I test my service worker?

Testing a service worker involves checking its registration, installation, activation, and functionality. You can use the browser’s developer tools to inspect the service worker’s lifecycle events. In Chrome, for example, you can use the Application panel in DevTools. You can also use the Fetch event to test how your service worker handles network requests.

Can service workers work with all types of HTTP requests?

Service workers can handle most types of HTTP requests, but not all. They can’t handle requests made with the Fetch API’s mode set to ‘no-cors’, for example. This is because service workers require access to the full response, and ‘no-cors’ requests only provide a limited, opaque response.

Why isn’t my service worker updating?

A service worker won’t update if there’s still a tab open that’s controlled by the old service worker. To force an update, you can either close all tabs using the service worker, or you can use the skipWaiting() method to bypass the waiting phase and immediately take control of the page.

Can service workers be used for real-time data?

Service workers are not ideal for real-time data because they are designed to handle network requests in the background. For real-time data, you would typically use WebSockets or Server-Sent Events, which maintain a persistent connection between the client and server.

How can I debug a service worker?

Debugging a service worker involves using the browser’s developer tools. In Chrome, you can use the Application panel in DevTools to inspect the service worker’s lifecycle events, network requests, and cache storage. You can also use console.log statements within your service worker code to output information to the console.

Can service workers be used with other APIs?

Yes, service workers can be used in conjunction with many other APIs to provide advanced functionality. For example, they can be used with the Cache API to enable offline support, the Push API for push notifications, and the Background Sync API for background data synchronization.

How secure are service workers?

Service workers are very secure because they are only allowed to run over HTTPS, ensuring that the service worker script and all network requests it handles are secure. They also have a limited lifespan and are terminated when not in use, reducing the risk of attacks.

Can service workers be used on all browsers?

As of now, service workers are supported by most modern browsers, including Chrome, Firefox, Safari, and Edge. However, they are not supported by Internet Explorer. You can check the current level of support on the Can I use website.

How can I unregister a service worker?

You can unregister a service worker using the ServiceWorkerRegistration.unregister() method. This will remove the service worker and clear all caches and databases it uses. However, it won’t take effect until all tabs controlled by the service worker have been closed.