Service Worker Offline Status

Ronnie Royston
3 min readJan 4, 2023

--

Introduction

This article demonstrates using Service Workers to display a customized offline view when the web browser has no network connectivity. Service workers are key to progressive web applications, websites that are installable to the homescreen as an app.

Challenges

Several. This is not a feature for beginners to deploy. Support for service workers remains spotty but, if not supported by the browser, the website will function fine. Troubleshooting can be difficult if you do not know how to force a reload on your browser. Custom Offline ViewIn other words, you will think you have deployed an update when in fact the previous service worker remains active.

Finally, since we are effectively inserting a man-in-the-middle between the browser and the network, there are security measures baked in to browsers that generate a variety of misleading errors if our service worker is not implemented precisely correct.

Setup and Installation

Install your service worker, a javascript file, via your websites regular JavaScript. You will want to check for browser support prior to installing the service worker as shown below.

if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}

Service Worker JavaScript Notes

A couple of key features are worth noting. Any file browsed to is automatically added to cache, in addition to the files specified in the array. Updating the CACHE_NAME tells the browser to reevaluate the files cached and deletes extraneous files. Also, we must set the redirect mode of the Request interface of the Fetch API to “manual”. Without this adjustment, the custom offline status page will not display.

The Service Worker JavaScript

The script below installs an array of files to the browsers cache. This makes these files available even in case of a network outage. Your array of files may be different. Adjust your script accordingly.

var CACHE_NAME = "htt-cache-v007";
var OFFLINE_URL = "/offline";
var REQUIRED_FILES = [
"/offline",
"/app.min.css",
"/app.min.js",
"/favicons/android-chrome-192x192.png",
"/favicons/android-chrome-512x512.png",
"/favicons/apple-touch-icon.png",
"/favicons/browserconfig.xml",
"/favicons/favicon-16x16.png",
"/favicons/favicon-32x32.png",
"/favicons/favicon.ico",
"/favicons/manifest.json",
"/favicons/mstile-150x150.png",
"/favicons/safari-pinned-tab.svg",
"/media/logo.png"
];
self.addEventListener('install', function(event) {
event.waitUntil(caches.open(CACHE_NAME).then(function(cache) {
return Promise.all(REQUIRED_FILES.map(function(url) {
return fetch(new Request(url, { redirect: 'manual' })).then(function(res) {
return cache.put(url, res);
});
}));
}));
});
self.addEventListener('fetch', function(event) {
if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
if(event.request.url.includes("your-domain-name.com")){
event.respondWith (
caches.match (event.request, {cacheName: CACHE_NAME}).then(function(resp) {
return resp || fetch(event.request).then(function(response) {
let responseClone = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
return response;
});
}).catch(function(error) {
return caches.open(CACHE_NAME).then(function (cache) {
return cache.match(OFFLINE_URL);
});
})
);
}
}
});
self.addEventListener('activate', function(event) {
var cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (cacheWhitelist.indexOf(key) === -1) {
console.log('Deleting out of date cache:', CACHE_NAME);
return caches.delete(key);
}
}));
})
);
});

Troubleshooting

One gotcha that I figured out was how to deal with clean URL’s, i.e., URL’s where the file extension is removed by the web server. For example, many websites are configured to hide the file extension “.html” or “.php” from the URL. The file extension is very important when using service workers. Include files in your service worker as they appear in your website. If your server requires the file extension, then include the file extension in your service worker script. If your server strips the file extension then do not include the file extension in your service worker script.

Remember to test your offline view across browsers. Use developer tools (Ctrl+Shift+i) to assist in monitoring the state of your browsers cache and service worker.

--

--

Ronnie Royston

Delivering refined solutions via vigorous practice. Tulane ('97), Cisco CCIE# 6824, Google Certified Professional Cloud Architect, and USPA Master Skydiver