Setup
- web-push version: 3.6.7
- Node.js version: v22.22.2
- Browser: Safari on iOS (PWA installed via Add to Home Screen)
- Push service: Apple (web.push.apple.com)
Problem
When VAPID keys are configured with a subject like mailto:openclaw@localhost, Apple's push service returns 403 Forbidden with {"reason":"BadJwtToken"}. The same code works perfectly with Google/FCM endpoints.
Root Cause
Apple requires the VAPID JWT sub claim to be a valid email domain or URL. @localhost is rejected as invalid. This was confirmed by testing:
mailto:openclaw@localhost → 403 BadJwtToken ❌
mailto:realuser@gmail.com → 201 ✅
https://myhost.tailscale.ts.net → 201 ✅
The JWT itself is cryptographically valid and verifies correctly with both jws.verify() and Node's native crypto.createVerify('SHA256').
Suggestion
The README should explicitly document that the VAPID subject must be a real email address or URL — not a placeholder. Many projects auto-generate VAPID keys with @localhost or the machine hostname, and this silently fails only on Apple's push service.
Related
Setup
Problem
When VAPID keys are configured with a subject like
mailto:openclaw@localhost, Apple's push service returns403 Forbiddenwith{"reason":"BadJwtToken"}. The same code works perfectly with Google/FCM endpoints.Root Cause
Apple requires the VAPID JWT
subclaim to be a valid email domain or URL.@localhostis rejected as invalid. This was confirmed by testing:mailto:openclaw@localhost→ 403 BadJwtToken ❌mailto:realuser@gmail.com→ 201 ✅https://myhost.tailscale.ts.net→ 201 ✅The JWT itself is cryptographically valid and verifies correctly with both
jws.verify()and Node's nativecrypto.createVerify('SHA256').Suggestion
The README should explicitly document that the VAPID subject must be a real email address or URL — not a placeholder. Many projects auto-generate VAPID keys with
@localhostor the machine hostname, and this silently fails only on Apple's push service.Related