Web push notifications allow users to opt-in to asynchronous messages from a server to a web application. The message can be sent at any time, even when the web application or the web browser is inactive. This W3C standard, based on the HTTP/2 protocol, is now supported by Chrome, Firefox, Edge, Opera, and Safari – but not by iOS.

In this blog post, I will discuss a minimal working example for implementing Web Push. The source code for the client application running in the web browser is available on GitHub. If you want to skip the technical stuff and just try it, a working sample for testing and sending push messages can be viewed here.

Web Push: Client Side

The workflow for subscribing to Web Push messages is as follows: as a prerequisite, a VAPID key pair has to be created, consisting of a public and a private key. A Web site or locally installed PWA can now request permission from the user to send messages to the web client. If this is allowed, the client application sends the public VAPID key, also called the applicationServerKey , to the push service that is defined for the web browser or web engine in use. Every browser uses its own push service but this is transparently taking care of when using the API. This is a code example for getting the permission and retrieving the subscription information:

function getPushPermission() {
navigator.serviceWorker.getRegistration().then(registration => {
registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlB64ToUint8Array(applicationServerKey)
}).then(subscription => {
const json = JSON.stringify(subscription.toJSON(), null, 2);
// success: send json to server
},
error => {
console.log(“error getting push permission”, error);
});
});
}

The push service returns the subscription information: an endpoint URL, an authorization key to authorize at the push service and a public encryption key to encrypt the message payload. The client application transmits this information to the Web server to be stored. This information looks like this (keys and URL abbreviated):

{
"endpoint": "https://updates.push.services.mozilla.com/wpush/v2/gAAAAA...",
"keys": {
"auth": "wvlsArhzWhluC2rHaMt8YA",
"p256dh": "BNWCKg1oxCstDq6V18RHI4ZiUA2GycC7k3S5H4MnishiQkw62Zy0elt9z..."
}
}

Web Push: Server Side

To send a push message the server encrypts the message payload with the p256dn key, signs it with the private VAPID key, and contacts the push service via the endpoint URL, authorizing with the auth key. To do this heavy lifting there are already quite a few libraries available, e.g. for Node.js, PHP, Python, and many other programming languages. Some of them are listed below.

As an example, we can do this with the Node.js library web-push and after installing the library with

npm -install web-push

you can use the following source code to send push messages:

const webpush = require(‘web-push’);
webpush.setVapidDetails(
‘mailto:example@yourdomain.org’,
vapidKeys.publicKey,
vapidKeys.privateKey
);
const pushSubscription = {
endpoint: ‘…..’,
keys: {
auth: ‘…..’,
p256dh: ‘…..’
}
};
webpush.sendNotification(pushSubscription, ‘Your Push Payload Text’);

The get valid keys and the endpoint URL, head over to the working client demo and subscribe to push messages. Don’t forget to allow push messages, when asked! Then replace the code for the definition of the variable pushSubscription with the content of the subscription data text field at the top of the page and also replace vapidKeys.publicKey and vapidKeys.privateKey with the respective VAPID keys at the bottom of the page. Running the code should now send a push message with the defined payload text.

Back to the Client

The push service sends the message to the client web browser or web engine. During the subscription process, the web page or PWA has to register a so-called service worker, a Javascript file that can run independently in the background. This service worker contains an event listener responsible for the display of the message. The basic code looks like this:

self.addEventListener(‘push’, event => {
event.waitUntil(
self.registration.showNotification(“Web Push Notification”, {
body: event.data.text(),
})
);
});

The showNotification function accepts an options object with many possible parameters, including an icon to be shown or a vibration pattern. Especially useful is the definition of action buttons to trigger events like opening a web page. See the demo code on GitHub for an example.

According to the Google documentation for Web Push, this API opens a whole new set of possibilities for you to re-engage with your users. Whether or not you share this optimism, push notifications are surely an interesting alternative or at least a complement to other methods like email newsletters.