You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web Push通知已接收但无法触发的技术求助

问题:Web Push通知无法触发

我基于webpush做了一个POC验证浏览器推送通知功能,页面有两个按钮分别用于订阅推送和测试通知:

  • 订阅流程已完成,日志能确认订阅信息已存储
  • 点击测试按钮会向/notify接口发起POST请求,返回200状态码
  • 已生成VAPID密钥对,公钥配置在index.html,私钥配置在server.js
  • Chrome能打印出worker中的console.log内容,但Chrome和Firefox都没实际弹出通知
  • 项目本地跑在3000端口,通过Nginx Proxy Manager反向代理到带有效SSL证书的子域名https://sub.mydomain.com,已启用WebSocket支持
  • 浏览器已允许该域名的推送权限,订阅时也成功获取了权限,Firefox开启Service Worker调试也没找到问题

相关代码

server.js

const express = require('express');
const webpush = require('web-push');
const path = require('path');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

// Serve static files from the 'public' directory
app.use(express.static(path.join(__dirname, 'public')));

const publicVapidKey = 'my pub key';
const privateVapidKey = 'my priv key';

webpush.setVapidDetails('mailto:noreply@mydomain.com', publicVapidKey, privateVapidKey);

let subscriptions = [];

app.post('/subscribe', (req, res) => {
    const subscription = req.body;
    subscriptions.push(subscription);
    console.log('subscriptions: ', subscriptions);
    res.status(201).json({});
});

app.post('/notify', (req, res) => {
    console.log('hit /notify');

    const payload = JSON.stringify({
        title: 'New Notification',
        message: 'You have a new notification!'
    });

    subscriptions.forEach(sub => {
        webpush.sendNotification(sub, payload).catch(err => {
            console.error('Error sending notification:', err);
        });
    });
    res.status(200).json({});
});

app.use(express.static('public'));

app.listen(3000, () => {
    console.log('Server started on port 3000');
});

worker.js

self.addEventListener('push', event => {
    console.log('push received');
    console.log(event);
    console.log(event.data);
    console.log(event.data.json());
    const data = event.data.json();
    // debugger;
    self.registration.showNotification(data.title, {
        body: data.message,
        icon: '/icon.png'
    });
    // tried this too:
    //const notification = new Notification(data.title, {
    //    body: data.message,
    //    icon: '/icon.png'
    //});
});

index.html(核心代码)

// Subscribe for push notifications
async function subscribeUser() {
    const registration = await navigator.serviceWorker.register('/worker.js', { scope: '/' });
    const subscription = await registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
    });
    await fetch('/subscribe', {
        method: 'POST',
        body: JSON.stringify(subscription),
        headers: { 'Content-Type': 'application/json' }
    });
    alert('Subscribed successfully!');
}

// Send a test notification
async function sendNotification() {
    await fetch('/notify', { method: 'POST' });
}

function urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
    const rawData = window.atob(base64);
    return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}

document.getElementById('subscribe').addEventListener('click', subscribeUser);
document.getElementById('notify').addEventListener('click', sendNotification);

排查与修复方案

1. 给Service Worker的推送事件添加waitUntil

Push事件处理需要确保异步操作被event.waitUntil()包裹,否则浏览器可能在通知展示前终止Service Worker。修改worker.js:

self.addEventListener('push', event => {
    console.log('push received');
    const data = event.data.json();
    // 用waitUntil包裹异步操作,确保Service Worker存活到通知展示完成
    event.waitUntil(
        self.registration.showNotification(data.title, {
            body: data.message,
            icon: '/icon.png'
        })
    );
});

2. 验证VAPID密钥对的正确性

  • 确保公钥和私钥是配对生成的,重新生成密钥对可以用web-push提供的命令:
    npx web-push generate-vapid-keys
    
  • 检查index.html中的publicVapidKey是否是原始公钥(不要包含额外的引号或空格),转换函数urlBase64ToUint8Array是否正确处理了密钥格式。

3. 检查通知资源与权限细节

  • 确认icon.png存在且可访问,路径/icon.png是否正确,若图标加载失败部分浏览器会静默不显示通知。
  • 手动验证浏览器通知功能:在控制台执行new Notification('测试', {body: '测试通知'}),确认系统和浏览器没有拦截通知。

4. 完善Nginx反向代理配置

确保Nginx Proxy Manager的配置正确传递请求头,添加以下自定义配置:

location / {
    proxy_pass http://localhost:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    # 确保WebSocket支持
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

5. 查看完整推送错误日志

在server.js的错误捕获中打印完整错误栈,定位推送服务返回的具体问题:

webpush.sendNotification(sub, payload).catch(err => {
    console.error('推送错误详情:', err, err.stack);
});

常见错误包括VAPID签名无效、订阅信息过期、推送服务返回404/410等。


内容的提问来源于stack exchange,提问作者David Fox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:34:53