Skip to content
ProfitTool
Back to all articles
Developer Tools
1 min read

How to Generate VAPID Keys with web-push, Node or OpenSSL

Generate the VAPID key pair that authenticates your push server to browser push services — using the web-push CLI, Node's crypto module, or OpenSSL.

ProfitTool Team

Developer Tools

What VAPID Keys Are

VAPID (Voluntary Application Server Identification) is an RFC 8292 standard for authenticating push notification servers. Instead of sharing an API key with each browser push service, your server proves its identity using a public/private key pair.

The public key is shared with the browser when a user subscribes to push notifications. The private key is kept secret on your server and used to sign JWT tokens sent to the push service.

Generating VAPID Keys

Using web-push (Node.js)

npm install web-push
npx web-push generate-vapid-keys

Output:

Public Key: BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U
Private Key: UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTWKGKls

Using openssl (any platform)

# Generate P-256 private key
openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem

# Extract public key
openssl ec -in vapid_private.pem -pubout -out vapid_public.pem

# Convert to raw base64url format (needed for VAPID)
openssl ec -in vapid_private.pem -noout -text 2>&1 | grep -A 3 "priv:" | tail -3 | tr -d ' :
'

Environment Variable Setup

VAPID_PUBLIC_KEY=BEl62iUYgUivxIkv69yViEuiBIa...
VAPID_PRIVATE_KEY=UUxI4O8-FbRouAevSmBQ6o18hgE4...
VAPID_SUBJECT=mailto:hello@yourdomain.com

Client-Side Subscription

const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
// Send subscription to your server
await fetch('/api/subscribe', {
  method: 'POST',
  body: JSON.stringify(subscription)
});

Key Security Notes

  • Generate new VAPID keys for each application
  • Never expose the private key in client-side code
  • Store keys in environment variables, never in source code
  • Keys are long-lived — rotation requires re-subscribing all users

What the Keys Actually Sign

The key pair is not sent to the push service on every request. Instead, your server builds a short-lived JWT, signs it with the private key using ES256, and sends both the token and the public key in one header:

Authorization: vapid t=<signed JWT>, k=<base64url public key>

RFC 8292 fixes three claims in that token. aud must be the origin of the push service you are calling (for example https://fcm.googleapis.com), sub must be a mailto: or https: contact URL so the push service can reach you, and exp must be no more than 24 hours in the future. A push service rejects a token whose exp sits further out than that — the most common cause of a 401 when the keys themselves are correct.

If a push request is failing, paste the t= value into the JWT Decoder to read the header and payload, or the JWT Claims Inspector to see the expiry as a countdown rather than a Unix timestamp. Note that VAPID keys and JWT segments are base64url, not standard Base64 — the Base64URL Encoder & Decoder handles the - and _ alphabet that a plain Base64 tool will reject.

For the claim rules themselves see JWT Expiration Explained, and for how the token is structured, What Is a JWT?.

Tags:#vapid#web push#push notifications#service worker#pwa

ProfitTool Team

Developer Tools

Written and maintained by the ProfitTool team. Where an article references a ProfitTool utility, the behaviour described is the behaviour of the tool on this site — you can open it and check the output yourself.

Try the Interactive Tools Mentioned

Free to use with no ProfitTool signup. Data handling varies by tool.