Node.js中WebPush.sendNotification调用googleapis端点报401授权头错误
Web Push 401错误排查与问题解答
错误信息
WebPushError: Received unexpected response code at IncomingMessage.<anonymous> (/Users/sepp/.../node_modules/web-push/src/web-push-lib.js:347:20) at IncomingMessage.emit (node:events:406:35) at endReadableNT (node:internal/streams/readable:1331:12) at processTicksAndRejections (node:internal/process/task_queues:83:21) { statusCode: 401, headers: { 'content-type': 'text/plain; charset=utf-8', 'x-content-type-options': 'nosniff', 'x-frame-options': 'SAMEORIGIN', 'x-xss-protection': '0', date: 'Wed, 01 Feb 2023 19:57:43 GMT', 'content-length': '40', 'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000', connection: 'close' }, body: 'authorization header must be specified.\n', endpoint: 'https://fcm.googleapis.com/fcm/send/duj-etc-etc' }
相关代码
import * as webPush from "web-push"; const subDetails = { endpoint: "https://fcm.googleapis.com/fcm/send/duja6etc-etc", expirationTime: null, keys: { p256dh: "BHtwM-etc-etc", auth: "aYkx0etc-etc" } } await webPush.sendNotification(subDetails, "test message");
排查过程
- 未配置VAPID,调用
webPush.generateRequestDetails(pushSub.details, args.msg)查看请求详情,发现请求中无授权头:{ method: 'POST', headers: { TTL: 2419200, 'Content-Length': 121, 'Content-Type': 'application/octet-stream', 'Content-Encoding': 'aes128gcm' }, body: <Buffer ....>, endpoint: 'https://fcm.googleapis.com/fcm/send/duj-etc-etc' } - 本地同时运行前端页面和后端服务器,响应头包含
'x-frame-options': 'SAMEORIGIN' - Firefox Dev Edition测试正常,但Chrome和Opera GX出现相同401错误;Opera桌面版暂不支持推送,但错误提示指向授权头问题
问题
- 本地环境有特殊要求吗?
- 需要满足什么条件才会包含授权头?
问题解答
1. 本地环境的特殊要求
本地环境本身无额外强制要求,但需注意两点:
- HTTPS环境:Chrome等浏览器要求推送服务必须在HTTPS环境下运行(
localhost例外,但FCM对本地请求的授权校验更严格),本地开发可使用自签名证书或工具暴露HTTPS地址。 - 避免跨域iframe加载:响应头包含
x-frame-options: SAMEORIGIN,若前端页面被嵌入跨域iframe,可能导致推送订阅凭证无效,影响后端请求的授权逻辑。
2. 包含授权头的条件
要让web-push自动添加授权头,必须满足以下核心条件:
- 配置VAPID密钥对:Chrome、Edge等使用FCM的浏览器强制要求VAPID(Voluntary Application Server Identification)授权。web-push库仅在配置VAPID后,才会生成并添加
Authorization头。配置示例:const vapidKeys = { publicKey: '你的VAPID公钥', privateKey: '你的VAPID私钥' }; webPush.setVapidDetails( 'mailto:你的邮箱地址', vapidKeys.publicKey, vapidKeys.privateKey ); - 订阅凭证有效:确保前端传递的
subDetails中endpoint、p256dh、auth均有效且未过期。Firefox允许无VAPID的请求,但Chrome/FCM强制要求,这也是两类浏览器表现差异的原因。
内容的提问来源于stack exchange,提问作者Seph Reed
相关产品推荐
相关产品推荐

