The Web Push process allows a website to send notifications to a user even when the site is not open. It begins when the browser asks the user for permission to receive notifications. If the user agrees, the browser creates a subscription with a push service, such as Firebase, Mozilla Push Service, or Apple Push Service. This subscription contains a unique endpoint URL, along with a public key and an authentication secret. The browser then provides this information to the web application.
The web application sends the subscription details to its server, where they are securely stored, often in a database. When the application wants to send a notification, the server prepares a message payload and encrypts it using the public key provided in the subscription. The server then sends this encrypted payload to the push service endpoint, authenticating the request using the VAPID (Voluntary Application Server Identification) standard.
The push service verifies the message and forwards it to the appropriate browser. Even if the website is closed, the browser can still receive the push message. When this happens, it activates the registered Service Worker in the background, which processes the message and displays a notification to the user.
In essence, Web Push creates a secure communication chain between the web application, the push service, and the browser, enabling reliable delivery of messages and notifications directly to users.
This repo demonstrates end-to-end Web Push with minimal dependencies:
- Front-end (Service-Worker + page script) acquires a subscription using the W3C Push API and sends it to the back-end.
- Back-end PHP stores that JSON and later builds a standards-compliant push request using
webpush_send.phpwhich implements VAPID, ECDH, HKDF and AES-GCM in ~400 lines of functions. - The push request is sent over HTTP/2 to the vendor’s push service (FCM for Chromium, Mozilla Push Service for Firefox, etc.). The browser then delivers a
pushevent to the Service-Worker which displays (or routes) a notification.
For an illustrated explanation of each layer see the linked technical docs below.
This repository shows a minimal, pure-PHP way to experiment with the Web Push API – both from the browser’s Service-Worker side and from the server that delivers the push messages. If you're looking for a PHP class implementation, I recommend web-push-libs/web-push-php implementation.
Technical deep-dives:
• Browser JavaScript flow
• Browser implementation details
• Low-level push request construction
- PHP 8.1+ with OpenSSL extension
- Any modern browser (Chrome, Firefox, Edge, Brave, Safari 17-plus)
Everything else is vendored – no Composer install needed for a quick demo.
php generate_vapid_keys.phpThis creates two URL-safe base64 files:
vapid_public.keyvapid_private.key← keep this one secret
php -S 127.0.0.1:8000Then visit http://127.0.0.1:8000/ and click Subscribe. The page will:
- Register
service-worker.js - Ask for notification permission
- Generate a Push Subscription and POST it to
handle_subscription.php
The subscription JSON lands in subscription.json (or similar).
Use webpush_send.php to send a notification to your browser.
Examples:
# Modern encoding – works in all current browsers
php webpush_send.php subscription.json aes128gcm
# Legacy old browsers compatibility
php webpush_send.php subscription.json aesgcmIf no parameters are provided, the script will read the client subscription keys from subscription.json (the file created by handle_subscription.php when you register your browser) and use the default encryption method (aes128gcm by default, or the legacy aesgcm).
You'll see a debug output as a result.
This project is a simple, pure-PHP implementation designed to easily test and understand Web Push technology.
There are some important points to consider if you plan to use this code in production:
- Never expose the
vapid_private.keyfile. - Store client subscription data in a secure location or in a database.
- In the example
app.js, the server’s public VAPID key is loaded fromvapid_public.key. In production, inject the key directly into the JavaScript file. - For production use,
webpush_send.phpshould be optimized. The current implementation is intentionally unoptimized to make the protocol easier to understand.
rm subscription.json vapid_public.key vapid_private.keyThis project is released under MIT license.
Happy pushing 📯