Blog · Web Push
VAPID keys explained: what they are and why they should be yours
VAPID keys identify whoever sends your push notifications and bind every subscription to that sender. What they are, why they should be yours and the rules to follow.
VAPID keys are a public/private key pair that identifies whoever sends your push notifications. The browser uses the public key when it creates each subscription, and from then on the push service only accepts messages for that subscription from whoever holds the private key. That's why whoever controls the private key effectively controls your subscriber list.
In short:
- VAPID (RFC 8292) is the standard way for a push sender to identify itself to the push services run by Google, Mozilla, Apple and Microsoft.
- The public key isn't secret: your site hands it to the browser when subscribing. The private key must never leave the system that sends.
- Every subscription is bound to one specific pair: change keys, or lose the ones held by a provider that won't let you export them, and your users have to subscribe again.
- Three rules: keep the private key secret, don't rotate it without a reason and declare a
mailto:orhttps:contact.- Before you pick a provider, check that you can export both the keys and the subscribers.
What VAPID is and what problem it solves
VAPID stands for Voluntary Application Server Identification and is defined in RFC 8292. It solves a specific problem: browser push services receive messages from senders all over the world, and they need to know who sent each one, be able to reach that sender if something goes wrong and, above all, stop anyone from messaging subscribers that aren't theirs. Instead of making you sign up with every service, VAPID lets you identify yourself with your own key pair.
Think of the keys as a seal. The public key is the seal's design, which anyone can see and compare; the private key is the physical stamp, which only the sender should hold. When a message reaches the push service, it checks for the right seal before delivering it.
One detail that clears up a lot of confusion: VAPID keys aren't the ones that protect the content. Every subscription also comes with its own encryption keys, created by the browser, so only that device can read the message. VAPID identifies the sender and encryption protects what the message says: separate pieces with separate jobs. To see where each one fits in an alert's journey, read what Web Push is and how it works.
The public key binds every subscription to its sender
When a user accepts notifications, your page gives the browser the VAPID public key (the Push API calls it the applicationServerKey). The browser passes it to its push service when creating the subscription, and the service records it next to that browser's address. From then on, the subscription only accepts messages from someone who can prove they hold the matching private key.
That binding has two practical consequences:
- Nobody else can message your subscribers, even if your list of addresses leaks. Without the private key, the push service rejects the messages.
- You can't change keys without losing subscriptions. Existing ones stay bound to the old pair. To move to the new one, every user has to come back to your site and subscribe again.
An example with round numbers: a site with 20,000 subscribers that switches keys doesn't lose the list, but it does lose the ability to message it. It only gets back the people who visit again and accept again, and it has no way to tell everyone else to come back, because push was the channel it would have used.
Why the keys should be yours
If you use a notification service, it usually creates and stores your VAPID keys. That's fine as long as you can take them with you. It's a problem if the provider won't let you export the private key: your subscriptions are bound to a key you don't control, and the day you want to switch providers you'll have thousands of valid addresses you can't write to. The only way out is asking every user to subscribe again.
Before you pick a provider, ask three questions:
- Can I export the VAPID private key, not just the public one?
- Can I export each full subscription, with its address and encryption keys, not just an internal ID?
- Are keys per project, so each site or product has its own?
If any answer is "no", the subscriber list belongs to the provider, not to you. The third question matters more than it looks: if you ever sell a product or hand it to another team, you'll want to give them its keys without giving away everyone else's.
Three practical rules
Keep the private key secret
It belongs in your platform's secret variables or a secrets manager, never in the repository, frontend config or a message in the team chat. Back it up: losing it means losing the ability to message every subscriber you have. And if it leaks, anyone who has it and knows their addresses could send them messages in your name.
Don't rotate without a reason
With most secrets, rotating on a schedule is good practice. With VAPID keys it's the opposite: every rotation forces your whole audience to subscribe again. Create them once per site or product and only change them after a leak. Watch out for deploy scripts that regenerate them on startup and new environments that create their own: they silently invalidate every subscription. Also use a separate pair for testing, because a subscription created on staging won't work with production keys.
Declare a contact
VAPID includes a contact field, the subject, which must be a mailto: address or an https: URL. The standard makes it optional, but Apple rejects sends whose contact isn't a URL or a mailto:, as its Web Push documentation notes. Without it you'd never reach Safari or iPhone users. Use a mailbox someone reads, such as mailto:push@yourdomain.com: it's how a push service operator can reach you if your traffic causes trouble.
How castly handles it
In castly, each project (one site or product) has its own VAPID key pair, created with the project and with the contact already set. The private key is stored encrypted and never touches your frontend: the SDK only uses the public key to subscribe, and sending only takes your API key.
And because the keys are yours, you can take them with you whenever you want. GET /v1/project/export, or the Export button in the dashboard, returns the VAPID pair with its contact and every subscriber with complete data:
curl https://castly.sh/v1/project/export \
-H "Authorization: Bearer $CASTLY_SECRET_KEY" \
-o export.json{
"vapid": { "publicKey": "BK-Lyx…", "privateKey": "…", "subject": "mailto:…" },
"subscribers": [
{ "id": "…", "endpoint": "https://…", "keys": { "p256dh": "…", "auth": "…" }, "userId": "user_123" }
]
}That file is what lets you switch providers without your users subscribing again. Store it as carefully as the private key, because it contains it. The full format is in the API reference.
Frequently asked questions
Are VAPID keys required?
In practice, yes. Chrome, Edge and Safari require a public key to create a subscription, and every current push service accepts VAPID. It's the standard way to identify yourself without signing up for each service.
Is the VAPID public key secret?
No. It ships in your page's code and anyone can see it. The only thing you need to protect is the private key.
Do VAPID keys expire?
No. They work indefinitely until you replace them. What does expire are individual subscriptions, when a user revokes permission or clears their browser data.
What should I do if the private key leaks?
Treat it as a security incident: the leaked pair can no longer be trusted and you'll have to move to a new one, which means getting your users to subscribe again. That's why the private key deserves more protection than almost any other secret.
Can I use the same keys on several sites?
Technically yes, because the key only identifies the sender. But it's a bad idea: one leak would hit every site at once, and you couldn't hand one site over without also handing over the key for the rest.