PHP Push Notifications (Step-By-Step Example)

Welcome to a tutorial on how to send push notifications in PHP. It is not a secret that web browsers are capable of displaying notifications, but just how can we send push notifications from a PHP server? Read on for the example!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

  • A copy of the Web Push library is not included in the zip file. Download your own – composer require minishlink/web-push.
  • Make sure that the PHP OpenSSL extension is enabled, run 2-vapid-keys.php to generate your own public/private keys.
  • Change the public key in 3-perm-sw.html to your own.
  • Change the subject, public, and private keys to your own in 5-push-server.php.
  • Access 3-perm-sw.html in your browser.
  • Take note that https:// is required for push notifications to work, http://localhost is an exception for testing.
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

SCREENSHOT

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

PHP PUSH NOTIFICATIONS

All right, let us now get into the example of how to send push notifications in PHP.

 

STEP 1) SERVER SETUP

1A) PHP WEB PUSH LIBRARY

Before we start with the code, let us deal with the foundations. First, we need to download the PHP Web Push library.

  • Install Composer package manager if you have not already done so.
  • Open the command line or terminal.
  • Navigate to your HTTP folder. For example, cd d:/http.
  • Run composer require minishlink/web-push.

That’s all. The Web Push library will be downloaded into the vendor/ folder.

 

1B) PHP OPENSSL

Next, enable the OpenSSL extension in PHP. The installation is different depending on your OS and build.

  • Linux
    • It is as simple as sudo apt-get install openssl.
    • Or yum install openssl for the CentOS users.
    • Then, php -i | grep -i openssl.
  • Windows – I am using XAMPP and OpenSSL is already included. But a hiccup got me stumped for a while.
    • Start > Search for “system environment” > “Edit the system environment variables” > “Environment variables”.
    • Add a new system variable called OPENSSL_CONF and point it to the openssl.cnf file. By default, it is located at xampp\php\extras\openssl\openssl.cnf.

For all other platforms and troubleshooting, please do your own research. It’s just too much to cover in this mini section.

 

 

STEP 2) GENERATE VAPID KEYS

2-vapid-keys.php
<?php
require "vendor/autoload.php";
use Minishlink\WebPush\VAPID;
print_r(VAPID::createVapidKeys());
D:\http>php 2-vapid-keys.php
Array
(
  [publicKey] => GENERATE-YOUR-OWN
  [privateKey] => GENERATE-YOUR-OWN
)

VAPID stands for Voluntary Application Server Identification. Simply put, generating a pair of public and private keys to prevent your push notifications from getting hijacked. This script only needs to run once, but you can always run this again to generate a new pair of keys.

P.S. If OpenSSL is not properly installed, createVapidKeys() will return a false.

P.P.S. The private key should only be used on the server side. Do not expose it publicly.

 

STEP 3) CLIENT PAGE

3A) GET PERMISSION TO SHOW NOTIFICATIONS

3-perm-sw.html
// (A) OBTAIN USER PERMISSION TO SHOW NOTIFICATION
window.onload = () => {
  // (A1) ASK FOR PERMISSION
  if (Notification.permission === "default") {
    Notification.requestPermission().then(perm => {
      if (Notification.permission === "granted") {
        regWorker().catch(err => console.error(err));
      } else {
        alert("Please allow notifications.");
      }
    });
  } 
 
  // (A2) GRANTED
  else if (Notification.permission === "granted") {
    regWorker().catch(err => console.error(err));
  }

  // (A3) DENIED
  else { alert("Please allow notifications."); }
}

Now that we have generated the keys, let’s put them aside for a minute. The first thing we need on the HTML page is to get the user’s permission to display notifications. Yes, we cannot proceed if notifications are blocked.

 

 

3B) REGISTER SERVICE WORKER

3-perm-sw.html
// (B) REGISTER SERVICE WORKER
async function regWorker () {
  // (B1) YOUR PUBLIC KEY - CHANGE TO YOUR OWN!
  const publicKey = "YOUR-PUBLIC-KEY";
 
  // (B2) REGISTER SERVICE WORKER
  navigator.serviceWorker.register("4-sw.js", { scope: "/" });
 
  // (B3) SUBSCRIBE TO PUSH SERVER
  navigator.serviceWorker.ready
  .then(reg => {
    reg.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: publicKey
    }).then(
      // (B3-1) OK - TEST PUSH NOTIFICATION
      sub => {
        var data = new FormData();
        data.append("sub", JSON.stringify(sub));
        fetch("5-push-server.php", { method: "POST", body : data })
        .then(res => res.text())
        .then(txt => console.log(txt))
        .catch(err => console.error(err));
      },
 
      // (B3-2) ERROR!
      err => console.error(err)
    );
  });
}

Once we have the user’s permission to display notifications, the next step is to register a service worker. For those who have never heard of it, a service worker is simply Javascript that runs in the background. Even when the user is not on the website itself. A couple of important points here:

  • (B1) A gentle reminder to change the public key to your own.
  • (B2) Register 4-sw.js as a service worker.
  • (B3) Take extra note of these.
    • reg.pushManager.subscribe(...) Pass in the public key and subscribe the service worker to the push server.
    • sub => { fetch... } Immediately send the subscribed worker sub to the server, and request a test push notification.

 

STEP 4) SERVICE WORKER

4-sw.js
// (A) INSTANT WORKER ACTIVATION
self.addEventListener("install", evt => self.skipWaiting());
 
// (B) CLAIM CONTROL INSTANTLY
self.addEventListener("activate", evt => self.clients.claim());
 
// (C) LISTEN TO PUSH
self.addEventListener("push", evt => {
  const data = evt.data.json();
  // console.log("Push", data);
  self.registration.showNotification(data.title, {
    body: data.body,
    icon: data.icon,
    image: data.image
  });
});

Well, this should be pretty self-explanatory. This service worker displays push notifications received from the server.

 

 

STEP 5) PHP PUSH SERVER

5-push-server.php
// (A) LOAD WEB PUSH LIBRARY
require "vendor/autoload.php";
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\WebPush;

// (B) GET SUBSCRIPTION
$sub = Subscription::create(json_decode($_POST["sub"], true));

// (C) NEW WEB PUSH OBJECT - CHANGE TO YOUR OWN!
$push = new WebPush(["VAPID" => [
  "subject" => "your@email.com",
  "publicKey" => "YOUR-PUBLIC-KEY",
  "privateKey" => "YOUR-PRIVATE-KEY"
]]);

// (D) SEND TEST PUSH NOTIFICATION
$result = $push->sendOneNotification($sub, json_encode([
  "title" => "Welcome!",
  "body" => "Yes, it works!",
  "icon" => "i-loud.png",
  "image" => "i-zap.png"
]));
$endpoint = $result->getRequest()->getUri()->__toString();

// (E) SHOW RESULT - OPTIONAL
if ($result->isSuccess()) {
  // echo "Successfully sent {$endpoint}.";
} else {
  // echo "Send failed {$endpoint}: {$result->getReason()}";
  // $result->getRequest();
  // $result->getResponse();
  // $result->isSubscriptionExpired();
}

Following up on the fetch call in the Javascript, this PHP script will handle the push notification.

  • (A) Load the Web Push library. Captain Obvious saves the day once again.
  • (B) Remember that the Javascript will send over a “subscribed service worker”? That’s that.
  • (C to E) We simply send a test push notification to the “subscribed service worker”.

The end. Congratulations, you have just sent out your first push message.

 

 

EXTRA BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

NOT HOW IT USUALLY WORKS…

Of course, this simplified example is not how a production server works.

  • In 3-perm-sw.html (B3) – We should send sub, the “subscribed service worker” to the server and save it into a database first.
  • At a later date, we manually call 5-push-server.php. This script will retrieve all subscribers from the database, and mass sends out the push notifications.
  • Also in 5-push-server.php (E), delete the user from the database when $result->isSubscriptionExpired() is true.

 

HOW TO “SAVE SUBSCRIBER TO DATABASE”

I left that out because MYSQL is not the only database in the world… Also because it’s a little off-topic for this example. If you want a “working example”, check out my open-source project – Storage Boxx, lib/LIB-Push.php.

 

I DENIED PERMISSION TO SHOW NOTIFICATIONS

Once denied, the browser will never prompt for permission again. The only way is to click on the icon next to the URL bar, and manually enable the notifications.

 

COMPATIBILITY CHECKS

A  “Grade A” browser is required for this example to work properly.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

17 thoughts on “PHP Push Notifications (Step-By-Step Example)”

  1. Bugfix: When using this code to send push notifications to the mobile version of Firefox, you’ll get an error that says “HTTP/1.1 413 Request Entity Too Large.”

    But it’s an easy fix. In line 16 of 5-push-server.php, add the following code:
    $push->setAutomaticPadding(false);

    By default, the WebPush API pads all the push notifications with extra random characters. For the very short test notification sent in this example, the padding takes up 4x more space than the actual message!

    WARNING: Do not use this fix if you’re transmitting sensitive information via push notification to the client (such as a 2-factor authentication key). Shorter messages mean weaker encryption. Longer messages are more secure, but some clients won’t get them. It’s a trade-off.

    Suppose you’re sending secret information to the client that you don’t want an attacker to intercept. In this case, don’t disable padding. Instead, decrease the padding limit like this:
    $MY_PADDING_LIMIT = 512;
    $webPush->setAutomaticPadding($MY_PADDING_LIMIT);

    Make sure to set $MY_PADDING_LIMIT to something larger than the longest message you’ll be sending to the client to make it hard for an attacker to guess the content. But remember… if you raise the limit too high, you run the risk of clients running under-resourced phones not receiving your notifications.

  2. I want to personnaly thank you the author for this page. I have followed step by step and it works well. I will implement it on my web site.

  3. Thank you for this post. I have never worked with service workers before and was wondering how the “connection” is made between server and device.

    Working with websockets a lot. What kind of data can be send with this besides plain text ? (Html, json for example)

      1. Another thing. When they subscribe and you save this in the database.

        When they unsubscribe. How do i know who unsubscribed so i can remove them from the database.

      2. Ah perfect, based on the http status you can remove a client from for example a database. Thank you, can work with that

    1. I will assume that you mean “automatically send notifications on publishing new post”. As “NOT HOW IT USUALLY WORKS” above –

      1) Save subscribers into a database first.
      2) On publishing a new post, get all subscribers and send a notification to all.

Leave a Comment

Your email address will not be published. Required fields are marked *