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

Web Push通知已发送但浏览器未显示问题求助

Web Push Notification 不显示问题排查与修复

直接原因:Service Worker 未触发通知显示

你的 Service Worker 仅监听 push 事件并打印日志,完全没有调用通知显示 API。浏览器不会自动弹出通知,必须在 push 事件处理函数中主动调用 self.registration.showNotification()。

修复后的 Service Worker 代码:

self.addEventListener('push', function(event) {
  let payload = {};
  if (event.data) {
    try {
      payload = event.data.json();
    } catch (e) {
      console.error('Payload 解析失败:', e);
    }
  }
  const title = payload.notification?.title || '新通知';
  const options = {
    body: payload.notification?.body || '通知内容',
    icon: payload.notification?.icon || 'icon.png'
  };
  // 用 waitUntil 保证通知显示操作完成
  event.waitUntil(
    self.registration.showNotification(title, options)
  );
});

后端加密与请求头的关键错误

虽然请求返回 201,但加密逻辑和请求头存在多处不符合 Web Push 规范的问题,导致浏览器无法解密并处理 payload:

1. 重复的 Content-Type 头

请求头中同时存在 application/octet-stream 和 application/json,这会造成解析冲突。由于发送的是加密后的二进制数据,应保留 application/octet-stream,删除重复的 application/json。

2. 缺失 Encryption 头

必须添加包含盐值的 Encryption 头,否则浏览器无法获取解密所需的盐:

'Encryption: salt=' . $salt, // $salt 为 base64url 编码后的盐值

3. Crypto-Key 头缺失 dh 参数

需要将本地生成的公钥(base64url 编码)作为 dh 参数加入 Crypto-Key 头,用于浏览器端的密钥推导:

'Crypto-Key: p256ecdsa=' . $keys['publicKey'] . ';dh=' . $localJWK->public_to_base64(),

4. 加密内容结构错误

  • 盐值处理错误:当前代码将 base64url 编码后的盐值放入内容,但实际需要的是原始二进制盐值。应直接使用 random_bytes(16) 生成的原始盐,而非编码后的字符串。
  • 缺少 GCM 标签:openssl_encrypt 返回的仅是密文,需要将生成的 $tag 追加到密文末尾——Web Push 要求 GCM 标签紧跟在密文之后。

修复后的内容构造代码:

// 生成原始二进制盐值
$saltRaw = random_bytes(16);
$salt = base64url_encode($saltRaw);

// ... 其他加密步骤保持不变 ...

$encryptedPayload = openssl_encrypt(json_encode($payload), 'aes-128-gcm', $contentEncryptionKey, OPENSSL_RAW_DATA, $nonce, $tag);
// 追加 GCM 标签到密文
$encryptedPayloadWithTag = $encryptedPayload . $tag;

// 构造符合规范的加密内容
$content = $saltRaw . pack('N', 4096) . pack('C', mb_strlen($decodedLocalPublicKey, '8bit')) . $decodedLocalPublicKey . $encryptedPayloadWithTag;

5. 修复后的完整请求头

$headers = [
    'TTL: ' . (60*60*12),
    'Urgency: normal',
    'Content-Type: application/octet-stream',
    'Content-Encoding: aes128gcm',
    'Content-Length: ' . mb_strlen($content, '8bit'),
    'Authorization: WebPush ' . $token,
    'Encryption: salt=' . $salt,
    'Crypto-Key: p256ecdsa=' . $keys['publicKey'] . ';dh=' . $localJWK->public_to_base64(),
];

额外验证步骤

  1. 打开浏览器控制台(Application > Service Workers),查看是否有解密失败的错误日志。
  2. 确认用户已授权通知权限(可通过 Notification.permission 检查)。
  3. 确保 icon.png 路径正确,且图片尺寸符合浏览器要求(建议 192x192px)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:15:18