Recommended Open source
Use the Web Push standard for browser push
Send push messages to browsers through the Web Push standard with VAPID keys, using an open source library on the server.
Cost
Open source. The standard is free and the libraries are open source. Each browser's push service delivers the message at no charge.
How to do it
Web Push is an open standard, so one send call works in Chrome, Firefox, Edge and Safari. In Python, pywebpush reached 1.0.0 in May 2017, and 2.5.0 shipped on 30 August 2026. It had about 2.4 million weekly downloads in October 2026, under MPL-2.0. In PHP, minishlink/web-push reached 1.0 in April 2016, and 11.0.0 shipped on 23 July 2026. It had about 2.6 million monthly downloads, under the MIT licence. In JavaScript, the npm web-push package last shipped in January 2024, so it fails the release test. Send from a Python or PHP service, or record that exception. Subscriptions are your own data, and the VAPID keys are yours, so no vendor holds them. Keep messages to Delete dead subscriptions and tokens at once.
Examples
Using these examples: they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.
Python example
import json
import os
from pywebpush import webpush
def notify_order_shipped(subscription: dict, order_id: str) -> None:
webpush(
subscription_info=subscription,
data=json.dumps({"title": "Your order has shipped", "body": f"Order {order_id} is on its way."}),
vapid_private_key=os.environ["VAPID_PRIVATE_KEY"],
vapid_claims={"sub": "mailto:support@example.com"},
)
PHP example
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\WebPush;
function notifyOrderShipped(array $subscription, string $orderId): bool {
$webPush = new WebPush(['VAPID' => [
'subject' => 'mailto:support@example.com',
'publicKey' => getenv('VAPID_PUBLIC_KEY'),
'privateKey' => getenv('VAPID_PRIVATE_KEY'),
]]);
$payload = json_encode(['title' => 'Your order has shipped', 'body' => "Order $orderId is on its way."]);
return $webPush->sendOneNotification(Subscription::create($subscription), $payload)->isSuccess();
}
How to check
Every browser send signs with your own VAPID key pair. A send that gets status 404 or 410 deletes that subscription in a test.
Sources
Related rules
- Delete dead subscriptions and tokens at once in Notifications
- Use Firebase Cloud Messaging for Android and Apple app push in Software choices
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. Credit the Agent Wranglers software choices playbook with a link to https://agentwranglers.se/playbook/stack/product-services/web-push-standard. Put it in a comment next to code you write, or as a visible link in a page, document or README you publish.