Some organizations restrict persistent connections like WebSockets, yet teams still need timely notifications even when the app isn’t open or focused.
With Web Push—the Push API, a Service Worker, and VAPID—servers can push messages reliably without keeping a socket alive, including when the page is backgrounded or closed.
Why Web Push
- Works in the background via a Service Worker and shows native notifications using the Notifications API for consistent, system‑level UX.
- Standards‑based, requires HTTPS, and uses VAPID keys so your server is identified securely to push services.
How it fits together
- App registers a Service Worker and requests notification permission from the user on a secure origin.
- App subscribes with Push Manager to get a unique subscription endpoint and keys for that browser/device.
- Server stores subscriptions and later sends payloads signed with VAPID using a lightweight library.
- The Service Worker receives the push event and displays a native notification immediately.
Client: register SW and subscribe
// Convert base64 VAPID public key to Uint8Array function base64ToUint8Array(base64) { const padding = '='.repeat((4 - (base64.length % 4)) % 4); const b64 = (base64 + padding).replace(/-/g, '+').replace(/_/g, '/'); const raw = atob(b64); const output = new Uint8Array(raw.length); for (let i = 0; i < raw.length; ++i) output[i] = raw.charCodeAt(i); return output; } async function subscribeToPush(vapidPublicKeyBase64) { const registration = await navigator.serviceWorker.register('/sw.js'); const permission = await Notification.requestPermission(); if (permission !== 'granted') return; const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: base64ToUint8Array(vapidPublicKeyBase64), }); await fetch('/api/push/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription), }); } The app subscribes via the Push API on a secure context and sends the resulting subscription to the backend for later use.
Service Worker: receive and notify
// /sw.js self.addEventListener('push', (event) => { const data = event.data ? event.data.json() : { title: 'Update', body: 'New alert' }; event.waitUntil( self.registration.showNotification(data.title, { body: data.body, icon: '/icon.png', data: data.url || '/', }) ); }); self.addEventListener('notificationclick', (event) => { event.notification.close(); const url = event.notification.data || '/'; event.waitUntil(clients.openWindow(url)); }); The Service Worker handles the push event payload and displays a native notification using the Notifications API.
Server (Node/Express): VAPID and send
// npm i express web-push import express from 'express'; import webpush from 'web-push'; const app = express(); app.use(express.json()); // 1) Configure VAPID (generate once and set via env) webpush.setVapidDetails( 'mailto:admin@example.com', process.env.VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY ); // 2) Store subscriptions (replace with MongoDB in production) const subscriptions = new Map(); app.get('/api/push/public-key', (_req, res) => { res.json({ publicKey: process.env.VAPID_PUBLIC_KEY }); }); app.post('/api/push/subscribe', (req, res) => { const sub = req.body; subscriptions.set(sub.endpoint, sub); res.status(201).json({ ok: true }); }); app.post('/api/push/send', async (req, res) => { const payload = JSON.stringify({ title: 'Policy update', body: 'Click to review changes', url: '/inbox', }); const results = []; for (const sub of subscriptions.values()) { try { await webpush.sendNotification(sub, payload); results.push({ ok: true }); } catch { results.push({ ok: false }); } } res.json({ sent: results.length }); }); app.listen(3000, () => console.log('Server running on 3000')); The web‑push library signs payloads with VAPID and delivers to each saved subscription endpoint, letting servers send messages without maintaining a persistent connection.
Practical tips
- Only request permission at meaningful moments to avoid prompt fatigue and improve opt‑in rates.
- Subscriptions can expire; handle send failures by pruning invalid endpoints and re‑subscribing when needed.
- Push requires HTTPS and secure contexts; keep VAPID keys safe and reuse the same key pair across deploys per environment policy
If WebSocket's are off the table, Web Push gives reliable, secure, background delivery with a small footprint—perfect for “must‑know” alerts in constrained environments.
Top comments (0)