PHP后端如何实现浏览器Web Push通知推送?

来源:HTML教程作者:BIT程序员头衔:程序员
导读:本期聚焦于BIT程序员创作的《PHP后端如何实现浏览器Web Push通知推送?》,敬请观看详情。Web Push 的核心在于它绕过了页面必须打开的限制,借助 Service Worker 与浏览器推送服务建立长连接通道。当 PHP 后端需要发送通知时,它并不是直接连接用户浏览器,而是将加密后的消息提交给浏览器厂商的推送服务,由推送服务负责最终投递。这意味着后端只需掌握一个订阅端点和对应的密钥,就能完成推送。整个链路涉及 VAPID 身份验证、消息加密、Service Worker 事件监听等多个环节。本文从底层机制入手,拆解浏览器订阅、推送服务转发、Service Worker 展示通知的完整链路,并提供可直接运行的 PHP 代码示例。你将学会如何使用 php-web-push 库快速实现推送,也会了解原生实现思路以及权限请求、订阅失效、消息体积限制等常见坑点。

Web Push 的核心在于它绕过了页面必须打开的限制,借助 Service Worker 与浏览器推送服务建立长连接通道。当 PHP 后端需要发送通知时,它并不是直接连接用户浏览器,而是将加密后的消息提交给浏览器厂商的推送服务(如 Chrome 使用的 FCM、Firefox 使用的 Mozilla autopush),由推送服务负责最终投递。这意味着后端只需掌握一个订阅端点(subscription endpoint)和对应的密钥,就能完成推送。整个链路涉及 VAPID 身份验证、消息加密、Service Worker 事件监听等多个环节,下面逐一拆解。

PHP后端如何实现浏览器Web Push通知推送?

Web Push 的技术基础:为什么需要 Service Worker 和 VAPID

Service Worker 是运行在浏览器后台的独立线程,即使页面关闭也能存活。它监听 push 事件,当推送服务把消息送达浏览器时,Service Worker 负责展示系统通知。没有 Service Worker,浏览器无法在页面不活跃时接收推送。因此,前端必须注册一个 Service Worker 脚本,并在其中监听 push 事件。

但仅仅有 Service Worker 还不够,服务器需要向推送服务证明自己的身份。VAPID(Voluntary Application Server Identification)就是为此设计的。它使用非对称加密:服务器生成一对公钥和私钥,公钥交给前端用于生成订阅,私钥保留在后端用于对推送请求签名。推送服务会验证签名,从而确认请求来自合法的应用服务器,也便于统计和限制滥用。

另外,推送消息在传输过程中必须加密,否则推送服务可以读取明文内容。加密依赖订阅对象中的 p256dh 和 auth 密钥,使用椭圆曲线 Diffie-Hellman(ECDH)算法协商出共享密钥,再用 AES-GCM 加密载荷。这些细节通常由成熟的库处理,但理解原理有助于排查问题。

前端实现:获取权限并生成订阅对象

前端的任务是注册 Service Worker、请求通知权限,并调用 PushManager.subscribe 生成订阅对象。订阅对象包含 endpoint 和 keys,必须发送到后端保存。下面是完整的 JavaScript 示例:

// 注册 Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').then(function(registration) {
    console.log('Service Worker 注册成功', registration);
  }).catch(function(err) {
    console.error('Service Worker 注册失败', err);
  });
}

// 请求通知权限
Notification.requestPermission().then(function(permission) {
  if (permission === 'granted') {
    // 获取订阅对象
    navigator.serviceWorker.ready.then(function(registration) {
      const applicationServerKey = urlBase64ToUint8Array('你的公钥');
      registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: applicationServerKey
      }).then(function(subscription) {
        // 将 subscription 发送到后端保存
        fetch('/save-subscription', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify(subscription)
        });
      });
    });
  }
});

// 辅助函数:将 base64url 公钥转为 Uint8Array
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;
}

订阅对象是一个 JSON,典型结构如下:

{
  "endpoint": "https://fcm.googleapis.com/fcm/send/xxxxx",
  "expirationTime": null,
  "keys": {
    "p256dh": "BNcRd...",
    "auth": "xxxxx"
  }
}

其中 endpoint 是推送服务的 URL,后端发送请求时直接 POST 到这个地址。p256dh 是客户端公钥,auth 是认证密钥,它们用于加密消息。需要注意的是,不同浏览器的订阅对象可能略有差异,但核心字段一致。一旦用户清空浏览器数据或卸载应用,订阅可能失效,因此后端必须处理推送失败并清理无效订阅。

权限请求要注意时机:应在用户有明确操作(如点击“开启通知”按钮)时请求,而不是页面加载时立即弹窗,否则容易被浏览器拦截或引起用户反感。如果用户之前拒绝过权限,再次调用 requestPermission 可能不会弹出对话框,需要引导用户到浏览器设置中手动开启。

PHP 后端发送推送:使用 php-web-push 库的完整流程

PHP 生态中最常用的 Web Push 库是 minishlink/web-push,它封装了 VAPID 签名、消息加密和 HTTP 请求。首先通过 Composer 安装:

composer require minishlink/web-push

安装完成后,在 PHP 脚本中创建 WebPush 实例并设置 VAPID 认证信息。公钥和私钥可以通过库提供的命令生成,或者使用在线工具。生产环境中建议将私钥存放在安全位置,不要硬编码在代码中。下面是一个完整的发送示例:

<?php
require 'vendor/autoload.php';

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

// VAPID 密钥对(实际应从配置或环境变量读取)
$publicKey = '你的公钥';
$privateKey = '你的私钥';
$subject = 'mailto:admin@ipipp.com'; // 联系邮箱,推送服务可能要求

$auth = [
    'VAPID' => [
        'subject' => $subject,
        'publicKey' => $publicKey,
        'privateKey' => $privateKey,
    ],
];

$webPush = new WebPush($auth);

// 假设从数据库获取到的订阅 JSON
$subscriptionJson = '{"endpoint":"https://fcm.googleapis.com/fcm/send/xxxxx","keys":{"p256dh":"BNcRd...","auth":"xxxxx"}}';
$subscription = Subscription::create(json_decode($subscriptionJson, true));

// 通知内容
$payload = json_encode([
    'title' => '新消息',
    'body' => '您有一条新的通知',
    'icon' => '/icon.png',
    'url' => '/detail/123'
]);

// 发送推送
$result = $webPush->sendOneNotification(
    $subscription,
    $payload
);

// 检查结果
if ($result->isSuccess()) {
    echo '推送成功';
} else {
    $reason = $result->getReason();
    echo '推送失败,原因:' . $reason;
    // 如果是 410 Gone,表示订阅已失效,应删除该订阅
    if ($result->getStatusCode() === 410) {
        // 从数据库删除 $subscriptionJson 对应的记录
    }
}

sendOneNotification 方法会完成消息加密、构建 Web Push 请求并发送到订阅端点。payload 必须是字符串,通常使用 JSON 格式,因为 Service Worker 收到后可以自定义展示。如果推送服务返回成功状态码(如 201),表示消息已被接受,但并不意味着用户一定看到了通知(例如用户关闭了系统通知)。

实际应用中,你可能需要批量发送。可以使用 sendNotifications 方法传入多个订阅,但注意不要一次性发送过多,避免触发推送服务的速率限制。对于失败的订阅,应根据状态码(特别是 404 和 410)及时清理,防止反复发送无效请求。

原生实现与常见问题排查

如果不想引入第三方库,也可以手动实现 Web Push 协议。核心步骤包括:生成 VAPID 密钥对、使用私钥对 JWT 签名(包含 aud、exp、sub 等声明)、根据 RFC 8291 进行消息加密(需要实现 ECDH 密钥协商和 AES-128-GCM 加密),然后向 endpoint 发送 POST 请求,头部包含 Authorization 和 Crypto-Key 等字段。虽然可行,但实现复杂且容易出错,因此绝大多数项目都应该优先使用成熟库。

开发过程中常见的问题有:本地开发时通知不显示?首先要确认浏览器是否支持 Notification API 和 Service Worker,并检查是否通过 HTTPS 访问(localhost 被视为安全上下文,可以使用)。其次检查 Service Worker 是否成功注册,以及在 push 事件中是否正确调用了 event.waitUntil 和 showNotification。Chrome 的开发者工具中 Application 面板可以查看 Service Worker 状态和推送订阅信息。

另一个高频问题是订阅失效。用户清除浏览器缓存、卸载 PWA 应用或推送服务端重置密钥都可能导致订阅无效。后端在发送推送后必须检查响应状态码,如果是 404 或 410,应立即从数据库中删除该订阅。此外,推送消息的 payload 大小有限制,通常为 4KB 左右,超限会被推送服务拒绝。对于需要大文本的通知,应使用标题和摘要引导用户点击后获取完整内容。

最后,Web Push 是提升用户留存的有效手段,但也要注意推送频率和内容相关性,过度推送会导致用户直接关闭通知权限。在 PHP 后端接入 Web Push 并不复杂,关键是要理解整个链条,并在前端、后端和 Service Worker 三个环节做好配合。

Web PushPHP浏览器通知修改时间:2026-09-19 02:51:55

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0919/59065.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。