Web push notifications with Node.js
The web-push package handles encryption and VAPID signing for you. With Express and a small database table, you can send standard push to every browser in under 50 lines.
- npm install web-push gives you sendNotification() with encryption and VAPID built in.
- Generate keys once with npx web-push generate-vapid-keys and keep the private key in env vars.
- Store each PushSubscription (endpoint + keys) server-side, keyed by endpoint.
- Delete subscriptions that return 404 or 410.
Install and configure
npm install web-push express
npx web-push generate-vapid-keys # prints a public and a private key once// push.js
const webpush = require('web-push');
webpush.setVapidDetails(
'mailto:[email protected]',
process.env.VAPID_PUBLIC_KEY,
process.env.VAPID_PRIVATE_KEY
);
module.exports = webpush;Save subscriptions with Express
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/push/subscriptions', async (req, res) => {
const { endpoint, keys } = req.body || {};
if (!endpoint || !keys?.p256dh || !keys?.auth) return res.sendStatus(400);
await db.subscriptions.upsert({ endpoint, p256dh: keys.p256dh, auth: keys.auth });
res.sendStatus(201);
});The browser side that produces this JSON is in the Push API example.
Send and clean up
const webpush = require('./push');
async function sendToAll(message) {
const payload = JSON.stringify(message); // { title, body, url, icon }
const subs = await db.subscriptions.all();
for (const s of subs) {
const subscription = { endpoint: s.endpoint, keys: { p256dh: s.p256dh, auth: s.auth } };
try {
await webpush.sendNotification(subscription, payload, { TTL: 3600, urgency: 'normal' });
} catch (err) {
if (err.statusCode === 404 || err.statusCode === 410) await db.subscriptions.remove(s.endpoint);
else console.error('push failed', err.statusCode, err.body);
}
}
}For large lists, send with limited concurrency (for example 50–100 in flight) and a queue, so a slow push service doesn’t block your app.
Useful options
| Option | Purpose |
|---|---|
TTL | Seconds to keep trying if the device is offline |
urgency | very-low … high |
topic | Replace an undelivered message with the same topic |
headers | Extra request headers if needed |
What these mean on the wire: the Web Push protocol.
A tiny concurrency limiter
async function sendAll(subs, payload, limit = 50) {
let i = 0;
const worker = async () => {
while (i < subs.length) {
const s = subs[i++];
try { await webpush.sendNotification(s, payload, { TTL: 3600 }); }
catch (e) { if (e.statusCode === 404 || e.statusCode === 410) await db.subscriptions.remove(s.endpoint); }
}
};
await Promise.all(Array.from({ length: limit }, worker));
}For bigger lists, move this into a job queue (BullMQ, a cron worker, etc.) and record sent/failed counts per campaign.
Production checklist
- Private key in env/secrets only; never in the client bundle.
- Rate-limit the subscription endpoint and validate input.
- Queue sends; retry 429/5xx with backoff; never retry 404/410.
- Log sent/failed counts per campaign; add click tracking via your notification URLs.
- Serve
sw.jsuncached from your root.
Rather not maintain this? Pusharoo gives you the same standards-based delivery with a dashboard, segments, schedules and automations, free (docs).
Questions
Does the web-push npm package need Firebase?
No. It speaks the standard Web Push protocol to every browser’s push service using VAPID.
Can I use web-push with TypeScript or ES modules?
Yes. Import it with import webpush from 'web-push' and use the same API; type definitions are available.
How do I send to thousands of subscribers quickly?
Use a job queue and limited concurrency, and remove expired subscriptions as you go so later sends are faster.
Keep reading
The Web Push API in JavaScript: a working example
Register a worker, ask on click, subscribe with VAPID, store the subscription and send — complete code.
VAPID keys for web push, explained
What the key pair does, how the signed JWT works, how to generate keys and why rotation breaks lists.
Web push notifications in PHP
Install with Composer, create VAPID keys, store subscriptions, queue and flush, remove expired endpoints.
Web push notifications in React and Next.js
Service worker in public/, a subscribe hook, a route handler with web-push, env keys and iPhone notes.
More in Developers: Service worker push events, done right · The Web Push protocol, explained · Laravel browser push notifications (no Firebase) · Declarative Web Push, explained · Firebase web push vs. standard web push · All guides
Free for life. No limits.
Unlimited subscribers, campaigns and automations. No badges. About five minutes to install.