Setting up service workers on Vultr
Service workers provide a way to run scriptable network proxies in the web browser, allowing developers to control how web pages respond to network requests, manage resources, and build robust and performant web applications. Learning to use service workers in your web application will help you build features like background data sync, pre-fetching resources your visitors will need next, processing data in one place instead of multiple pages, and other exciting functionalities.
In this article, we'll learn about the lifecycle of a service worker and why it's essential to understand how they work. We'll also create an example web application hosted on Vultr, including HTTPS via Nginx and Let's Encrypt, which we need to allow the service worker to run on visitors' browsers.
Service worker lifecycle
Service workers are event-driven JavaScript background tasks that run separately from the main thread of a web page. They act as intermediaries between the web page and the network to provide features like offline support, push notifications, and background synchronization.
Understanding the lifecycle of a service worker is crucial for effectively implementing and managing them in web projects. This lifecycle consists of three main phases:
- Registration: The initial step is the registration phase where we check if the browser supports service workers.
- Installation: After registration, when the user visits the website the service worker is immediately downloaded for the service worker controlled URLs and a cache is created for the specified URLs. Whenever there's a new version of the website, installation will be triggered and the new version is installed in the background.
- Activation: During the activation phase, the service worker takes control of the web pages within its scope and begins intercepting network requests. This phase is typically an appropriate stage to clean up outdated caches and perform any necessary initialization tasks.
Setting up the project directory on Vultr
To begin, deploy a server by following the steps in the Deploying a server on Vultr section in our previous article. No specific marketplace application is needed for this project. Next, let's proceed to access the server terminal via SSH and set up a project for our web application. We'll make a landing page with a CSS stylesheet and add a script to our main JavaScript file that will be used to check if the browser supports service workers and register the service worker.
We'll be using the Nano text editor to create and edit our project files on the server. You can check the shortcuts cheatsheet for help using Nano.
-
Create a project directory, and navigate into it.
bashmkdir sample cd sample
-
Create an HTML file.
bashnano index.html
-
Copy and paste the code below into the index.html file.
html<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Service Worker App</title> <link rel="stylesheet" href="/main.css" /> </head> <body> <h1>Welcome to My Service Worker App</h1> <p>This is a sample paragraph with <a href="#">a link</a>.</p> <script src="/app.js"></script> </body> </html>
-
Save and exit the file.
-
Create a CSS file.
bashnano main.css
-
Copy and paste the code below into the main.css file.
cssbody, h1 { margin: 0; padding: 0; } h1 { text-align: center; margin-top: 50px; } p { text-align: center; } body { background-color: #add8e6; font-family: Arial, sans-serif; } a { color: #00ff7b; } a:hover { text-decoration: underline; }
-
Save and close the file.
Creating a service worker
Now that you have set up a project directory, in this section you'll explore how a service worker is registered, installed, and activated.
Installing and activating a service worker
Let's add a basic service worker to our project that our main JavaScript app can interact with:
-
In the
sample
directory, create a JavaScript file:bashnano sw.js
-
Copy and paste the code below into the
sw.js
file.jsconst CACHE_NAME = "my-cache-v1"; const urlsToCache = ["/", "/index.html", "/main.css"]; self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { console.log("Opened cache"); return cache.addAll(urlsToCache); }), ); }); self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((cacheName) => { return cacheName !== CACHE_NAME; }) .map((cacheName) => { return caches.delete(cacheName); }), ); }), ); }); self.addEventListener("fetch", (event) => { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } return fetch(event.request); }), ); });
-
Save and exit the file.
In the code above, we are hooking into the three events we described earlier. When an install event is fired, we open a cache for URLs we're interested in controlling. We use the activate event to clear out any old caches we may have. Finally, we have a fetch event that checks if the resource requested by the user is cached; otherwise, it fetches the resource from the network and returns it.
Registering a service worker
In this section, you'll learn how to check if a browser supports service workers, add an event listener to register the service worker, and give an alert once the service worker is registered.
-
Create a JavaScript file.
bashnano app.js
-
Copy and paste the code below into the
app.js
file.jsif ("serviceWorker" in navigator) { window.addEventListener("load", function () { navigator.serviceWorker .register("/sw.js") .then(function (registration) { alert( "Service Worker registration successful with scope: " + registration.scope, ); if (registration.installing) { console.log("Service worker installing"); } else if (registration.waiting) { console.log("Service worker installed"); } else if (registration.active) { console.log("Service worker active"); } }) .catch(function (err) { alert("Service Worker registration failed: " + err); }); }); }
-
Save and close the file.
In the code above, the if ('serviceWorker' in navigator)
conditional checks if the browser supports service workers. If we have support for service workers, we can go ahead with registration using the navigator.serviceWorker.register()
method given the script located at /sw.js
. We're sending an alert that prints the scope URL and logging lifecycle status to the console.
Serving a web app on a Vultr server
In this section, you'll learn how to create a systemd service that starts automatically when the system boots up and runs a Python HTTP server that serves the web application.
-
Exit the
sample
directory.bashcd ..
-
Create a Python file.
bashnano http_server.py
-
Copy and paste the code below into the
http_server.py
file.pythonimport http.server import socketserver import os PORT = 8000 DIRECTORY = "sample" sample_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sample") mime_types = { ".html": "text/html", ".css": "text/css", ".js": "application/javascript", } class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def guess_type(self, path): _, ext = os.path.splitext(path) return mime_types.get(ext.lower(), "application/octet-stream") Handler = MyHTTPRequestHandler os.chdir(sample_dir) with socketserver.TCPServer(("", PORT), Handler) as httpd: print("Serving at port", PORT) httpd.serve_forever()
-
The above code starts a Python HTTP server in our
sample
directory on port8000
. -
Save and exit the file.
-
Allow permission to execute the
http_server.py
as a script.bashchmod +x http_server.py
-
Create a service worker service file.
bashsudo nano /etc/systemd/system/pythonserver.service
-
Paste the following content into the service file. Make sure to replace the
User
andGroup
values with your actual values. Also, replaceWorkingDirectory
with the actualsample
directory path andExecstart
with the actualhttp_server.py
path.[Unit] Description= Daemon for Sample Demo Application After=network.target [Service] User=example_user Group=example_user WorkingDirectory= /example_user/sample ExecStart= /usr/bin/python3 /example_user/http_server.py [Install] WantedBy=multi-user.target
-
Start the service.
bashsudo systemctl start pythonserver
-
Verify the status of the service.
bashsudo systemctl status pythonserver
-
The output will look like this.
● pythonserver.service - Daemon for Sample Demo Application Loaded: loaded (/etc/systemd/system/pythonserver.service; disabled; preset: enabled) Active: active (running) since Fri 2024-04-05 10:29:31 UTC; 3s ago Main PID: 10181 (python3) Tasks: 1 (limit: 1011) Memory: 8.6M CPU: 43ms CGroup: /system.slice/pythonserver.service └─10181 /usr/bin/python3 /example_user/http_server.py
-
Enable the service to start automatically whenever the system boots up.
bashsudo systemctl enable pythonserver
Securing a web app on Vultr using HTTPS
Service workers only work in HTTPS contexts due to security reasons. This prevents man-in-the-middle attacks and reduces the risk of data interception and tampering over the network.
The first step is configuring a reverse proxy for your web application. We can use Nginx for efficient request handling and load balancing. Next, you need to request a free SSL certificate from Let's Encrypt to add HTTPS. The steps to perform this are described in our previous article Configuring Nginx as a reverse proxy server and Installing an SSL certificate using Certbot.
Once you have applied HTTPS, visit your application's URL, and if everything's working, you'll see an alert with the service worker scope. You can also check your browser's developer console to see lifecycle events. The first time you visit the page, the cache will be opened and the service worker will be installed. You can refresh the page to see the service worker is activated and the webpage can be accessed by the user while being offline. To test this you can turn off your internet connection and refresh the page again.
Conclusion
In this article, we learned what service workers are and the different phases of their lifecycle. We also learned how to create a web application with a service worker that adds offline functionality to specific pages and resources. Using service workers can significantly improve the user experience by allowing users to access content even when they're offline.
This is a sponsored article by Vultr. Vultr is the world's largest privately-held cloud computing platform. A favorite with developers, Vultr has served over 1.5 million customers across 185 countries with flexible, scalable, global Cloud Compute, Cloud GPU, Bare Metal, and Cloud Storage solutions. Learn more about Vultr