Library / SDK
web-push-libs/web-push-php avatar
web-push-libs/web-push-php

web-push-php: sending browser push notifications from PHP, with VAPID handled

Web Push library for PHP

1,898 stars312 forksPHPMIT

At a glance

What is it?
The Minishlink web-push library is a PHP implementation of the Web Push protocol that queues notifications, flushes them in one pass and reports each result, and it authenticates to the push services with VAPID keys so the browser can verify who sent the message.
Who is it for?
web-push-php fits a PHP application that needs to reach browsers with push notifications and wants the VAPID, payload encryption and subscription handling done for it, especially one that can accept a send-and-report API instead of handling the push services directly.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 44 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library does, and where the browser fits

WebPush is a library for PHP that sends push messages to endpoints as described in the Web Push protocol, and the protocol itself is specified in RFC 8030.

The flow is the standard one. The browser exposes a PushSubscription object, your server stores it, and the server later POSTs an encrypted payload to the subscription's endpoint. The push service delivers it, and the browser receives the message, which can then create a notification using the service worker and the Notifications API.

So the library's job sits in the middle: it holds the VAPID credentials, encrypts the payload for the subscription, talks to the push service over HTTP, and reports what happened to each message. The service worker side is your code, not this library's, which is worth knowing before you estimate the work.

The naming is a little confusing and the README sorts it out. The repository is web-push-libs/web-push-php, and the Composer package is minishlink/web-push, with the namespace Minishlink\WebPush. The example project referenced in the README is Minishlink/web-push-php-example, described as a complete example with an HTML and JavaScript front end and a PHP back end.

The project is MIT licensed, the primary language is PHP, the last push was on 2026-09-09, and the recent releases are v11.0.0 from 2026-07-23, v10.1.0 from 2026-05-28 and v10.0.3 from 2026-03-09.

The runtime requirements and the version branches

The requirements are PHP 8.2 or newer plus four extensions.

bcmath and/or gmp are listed as optional but better for performance, which is the payload encryption doing big integer arithmetic on the elliptic curve key agreement. mbstring is required. curl is required, because that is how the requests reach the push service. And openssl with elliptic curve support is required, because that is what performs the encryption.

The version table is the part that catches people out, because the library has a long history and the branches line up with PHP versions rather than with library features.

There is no support and maintenance for older PHP versions, however you are free to use the following compatible versions: PHP 5.6 or HHVM gets v1.x, PHP 7.0 gets v2.x, PHP 7.1 gets v3.x to v5.x, PHP 7.2 gets v6.x, PHP 7.3 and 7.4 get v7.x, PHP 8.0 or OpenSSL without elliptic curve support gets v8.x, and PHP 8.1 gets v9.x.

Two entries in that table are worth pausing on. The v8.x branch exists for PHP 8.0 users and for anyone whose OpenSSL lacks elliptic curve support, which means the encryption path is genuinely version dependent rather than the README being conservative. And the library has jumped from v9.x to v11.0.0, so the current line is not simply the next number after the previous one.

The README also says it is only compatible with the latest version, and that each version of the library has a git tag where the corresponding README can be read. So if you are on an older branch, the documentation you want is in the tag, not on the default branch.

Subscriptions, and the four shapes browsers have sent over the years

The Subscription object is where most of the friction lives, and the README is unusually thorough about it because the format has changed several times.

The current format has an endpoint plus a keys object with p256dh and auth, which is the current PushSubscription format, and the comment notes that browsers might change this in the future. There is also an old Firefox PushSubscription format with publicKey and authToken, an old Chrome PushSubscription format with just an endpoint, and an old PushSubscription format with publicKey, authToken and an optional contentEncoding.

The library handles all of them behind the same Subscription::create call, which is the point. A subscription created years ago on an old browser still works, and a subscription created on a current browser does not need a different code path.

The contentEncoding key is the optional one, defaulting to Subscription::defaultContentEncoding, and the values are the ones in PushManager.supportedContentEncodings, with aesgcm given in the example. That value selects the payload encryption scheme, and getting it wrong produces a notification the browser silently discards rather than an error, so it is worth setting explicitly when you store a subscription.

There is also a convenience for the common case. You can store the client side PushSubscription object, calling toJSON on it, as-is and then create a WebPush\Subscription from it:

php
<?php

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

// store the client-side `PushSubscription` object (calling `.toJSON` on it) as-is and then create a WebPush\Subscription from it
$subscription = Subscription::create(json_decode($clientSidePushSubscriptionJSON, true));

That means the server can keep the browser's own JSON in the database without translating it, which removes a class of migration bug when the browser format changes again.

Queue, flush, and reading the report

The send API has two shapes, and the batch one is the interesting one.

You create a WebPush instance, queue notifications against it, and then flush. Queuing first and flushing second means one set of connections and one batch of work for many messages, and each queued notification carries a subscription and an optional payload that defaults to null.

php
$webPush = new WebPush();

// send multiple notifications with payload
foreach ($notifications as $notification) {
    $webPush->queueNotification(
        $notification['subscription'],
        $notification['payload'] // optional (defaults null)
    );
}

Then flush returns a report per message, and the report is the API you should actually build your error handling on:

php
foreach ($webPush->flush() as $report) {
    $endpoint = $report->getRequest()->getUri()->__toString();

    if ($report->isSuccess()) {
        echo "[v] Message sent successfully for subscription {$endpoint}.";
    } else {
        echo "[x] Message failed to sent for subscription {$endpoint}: {$report->getReason()}";
    }
}

So each report gives you the request that was made, the endpoint it went to, a success flag, and a reason on failure. The reason matters because push endpoints expire and return an error status when they do, and the correct response is to delete the subscription from your database rather than to retry. A library that returned a single boolean would not let you tell those two cases apart.

For a single message there is a direct variant, sendOneNotification, which takes the subscription and the optional payload and returns the same report object. The README shows both, and the pattern to take from it is that every send produces a report, whether it was queued or sent directly.

VAPID, and the key that must never change

Browsers need to verify your identity, and the standard for that is VAPID, defined in RFC 8292. You need to create and provide a public and private key for your server, and the documentation is emphatic that these keys must be safely stored and should not change.

The subject property is where the practical experience diverges from the specification. According to the standard it is optional to provide contact details by the subject property, but in practice all browsers require a valid subject, which can contain an email address or an available https website, and it should not change.

There are three ways to supply the keys, and the code shows all of them:

php
$auth = [
    'VAPID' => [ // Recommended.
        'subject' => 'mailto:[email protected]', // Must be an email beginning with mailto: or available https website address.
        'publicKey' => '~88 chars', // Uncompressed public key P-256 encoded in Base64-URL.
        'privateKey' => '~44 chars', // In fact the secret multiplier of the private key encoded in Base64-URL.
    ],

Passing the keys directly is the recommended option. The alternatives are a pemFile, a path to a PEM file you can link to on your filesystem, or a pem, the content of the file hardcoded. The public key is the uncompressed P-256 key in Base64-URL, about 88 characters, and the private key is the secret multiplier of the private key in Base64-URL, about 44 characters.

The keys are generated with openssl, and the command sequence is in the README:

bash
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem
$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt

The prime256v1 curve is the P-256 curve the standard requires, and the second command does the conversion to the uncompressed Base64-URL form the browser expects, stripping the padding and swapping the URL-unsafe characters.

One last point from the documentation is a warning about the wider ecosystem: browser manufacturers may use additional verification methods to prevent abuse of the push service. In other words, VAPID being valid is necessary but not always sufficient, and a delivery that fails for no visible reason may be a push service decision rather than a bug in your code.

Editorial conclusion

web-push-php fits a PHP application that needs to reach browsers with push notifications and wants the VAPID, payload encryption and subscription handling done for it, especially one that can accept a send-and-report API instead of handling the push services directly. It is a poor fit if you are on an old PHP version, because the current line requires PHP 8.2 and older lines are only maintained for the older runtimes, and a poor fit if you need push to a native mobile app, since the protocol here is the browser one. Before adopting, read the version table in the requirements section so you pick the branch that matches your runtime, and generate a dedicated VAPID key pair, because the keys must not change once browsers have seen them.

Frequently asked questions

What does the web-push-php library do?

It is a PHP implementation of the Web Push protocol that sends push messages to browser endpoints as described in RFC 8030. You store the browser's PushSubscription, queue notifications against a WebPush instance, flush them, and read a report per message that tells you whether it succeeded and why it failed.

What does the web-push-php library require?

PHP 8.2 or newer, with the bcmath and/or gmp extensions, which are optional but better for performance, plus mbstring, curl, and openssl with elliptic curve support. Older PHP versions are not supported or maintained, but compatible branches exist: v1.x for PHP 5.6 or HHVM, v2.x for 7.0, v3.x to v5.x for 7.1, v6.x for 7.2, v7.x for 7.3 and 7.4, v8.x for 8.0 or OpenSSL without elliptic curve support, and v9.x for 8.1.

How do I authenticate a PHP push server with VAPID?

You create a public and private key pair for your server and pass them to the WebPush constructor along with a subject, which must be a mailto email address or an available https website even though the standard treats it as optional. The keys can be passed directly, which is recommended, or supplied as a PEM file path or as PEM content.

How do I generate VAPID keys for web-push-php?

With openssl, using the prime256v1 curve. The README gives openssl ecparam -genkey -name prime256v1 -out private_key.pem, followed by openssl ec -in private_key.pem -pubout -outform DER piped through tail -c 65 and base64 to produce the uncompressed Base64-URL public key, and the keys must be stored safely and never changed once browsers have seen them.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. web-push-libs/web-push-php on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/web-push-libs-web-push-php.svg)](https://hysenlabs.com/projects/web-push-libs-web-push-php)

Was this useful?