Web Push协议是一套允许服务端向浏览器推送消息的标准化协议,结合JavaScript和Service Worker能力,可实现网站在用户未活跃访问时也能发送通知,广泛应用于消息提醒、活动通知等场景。

Web Push协议核心架构
Web Push协议的整体流程分为三个核心环节,分别是前端订阅、服务端推送、浏览器接收处理,三者通过标准化的接口和加密规则实现安全通信。
核心参与角色
- 浏览器:负责执行Service Worker脚本,接收推送消息并展示通知
- 前端页面:通过JavaScript调用浏览器API生成推送订阅信息
- 推送服务端:存储订阅信息,按照协议规范加密并发送推送消息
- 推送服务:由浏览器厂商提供的中间服务,负责将服务端的消息转发给对应浏览器
协议核心特性
- 基于HTTPS传输,保障通信安全
- 消息内容需要加密,避免中间环节泄露
- 支持离线推送,用户关闭页面也能接收消息
前端订阅推送的实现步骤
前端需要先注册Service Worker,再申请通知权限,最后生成推送订阅信息并发送给服务端存储。
1. 注册Service Worker
Service Worker是推送通知的接收载体,需要在页面加载时完成注册。
// 检查浏览器是否支持Service Worker
if ('serviceWorker' in navigator && 'PushManager' in window) {
// 注册Service Worker文件
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('Service Worker注册成功', registration);
// 注册成功后申请推送订阅
return registration.pushManager.subscribe({
userVisibleOnly: true, // 推送消息必须显示通知
applicationServerKey: urlBase64ToUint8Array('你的VAPID公钥') // VAPID公钥转换后的数组
});
})
.then(subscription => {
// 将订阅信息发送到服务端存储
fetch('/save-subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
});
})
.catch(err => {
console.error('推送订阅失败', err);
});
}
// VAPID公钥转换工具函数
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
2. Service Worker接收推送消息
Service Worker脚本需要监听push事件,接收到消息后展示通知。
// sw.js 文件内容
self.addEventListener('push', event => {
const data = event.data.json(); // 解析推送的消息内容
const options = {
body: data.body,
icon: data.icon || '/default-icon.png',
badge: data.badge || '/default-badge.png',
data: {
url: data.url || '/' // 点击通知跳转的地址
}
};
// 展示通知
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// 监听通知点击事件
self.addEventListener('notificationclick', event => {
event.notification.close();
// 打开对应的页面
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
服务端推送消息的实现
服务端需要存储前端的订阅信息,按照Web Push协议规范加密消息,再调用推送服务接口发送消息。
1. 存储订阅信息
服务端接收到前端发送的订阅信息后,需要持久化存储,后续推送时根据订阅信息发送消息。订阅信息包含以下核心字段:
| 字段名 | 说明 |
|---|---|
| endpoint | 推送服务的地址,不同浏览器的推送服务地址不同 |
| keys.p256dh | 浏览器生成的公钥,用于消息加密 |
| keys.auth | 认证密钥,用于验证消息合法性 |
2. 消息加密与推送
Web Push协议要求消息必须经过加密,常用的Node.js实现示例如下:
const webpush = require('web-push');
const express = require('express');
const app = express();
app.use(express.json());
// 配置VAPID密钥,需要提前生成
const vapidKeys = {
publicKey: '你的VAPID公钥',
privateKey: '你的VAPID私钥'
};
webpush.setVapidDetails(
'mailto:admin@ipipp.com', // 联系邮箱
vapidKeys.publicKey,
vapidKeys.privateKey
);
// 存储订阅信息的数组,实际项目中使用数据库存储
let subscriptions = [];
// 接收前端发送的订阅信息
app.post('/save-subscription', (req, res) => {
subscriptions.push(req.body);
res.json({ success: true });
});
// 推送消息接口
app.post('/send-notification', (req, res) => {
const { title, body, url } = req.body;
const payload = JSON.stringify({
title,
body,
url
});
// 遍历所有订阅信息发送推送
const promises = subscriptions.map(subscription => {
return webpush.sendNotification(subscription, payload)
.catch(err => {
console.error('推送失败', err);
// 如果订阅失效,从存储中移除
if (err.statusCode === 410) {
subscriptions = subscriptions.filter(sub => sub.endpoint !== subscription.endpoint);
}
});
});
Promise.all(promises).then(() => {
res.json({ success: true });
});
});
app.listen(3000, () => {
console.log('服务端运行在3000端口');
});
常见问题与解决方案
- 通知权限被拒绝:需要在用户操作触发时申请权限,不要页面加载就直接申请,提升用户接受度
- 消息推送失败:检查VAPID密钥是否正确,消息是否按照规范加密,订阅信息是否完整
- 离线无法接收:确保Service Worker注册成功,且订阅时
userVisibleOnly参数设置为true
注意:不同浏览器的推送服务存在差异,测试时建议使用Chrome、Firefox等主流浏览器,且必须在HTTPS环境下运行,本地开发可以使用localhost绕过HTTPS限制。
JavaScriptWeb_Push推送通知Service_Worker修改时间:2026-07-24 06:18:32