Updated, 2016-02-20: The Push service now requires an explicit “TTL” header on requests to an endpoint. The article has been updated to reflect this. More details on the Mozilla Services Blog.
Have you ever wished that a website could notify you when something important happened, even if you didn’t have the site open? Maybe you’ve got an incoming WebRTC call, an instant message, or a financial update. Perhaps your city just declared an emergency snow plowing schedule.
Sometimes you just want to know when something happens.
That’s what Web Push does. It’s available now in Firefox 44.
What does Web Push look like?
As long as your browser is running, it can receive notifications from websites, even without having that site open. This means you can close your email tab and still find out when a new message arrives. That’s a huge win for memory usage, performance, and battery life.
Notifications from websites are indistinguishable from native notifications, and Mozilla’s Service Worker Cookbook has several live demos where you can see this for yourself.
Much like with geolocation or webcam access, Web Push requires explicit, revokable permission before a website can show notifications to a user.
What about privacy?
Web Push works by maintaining a persistent connection to a Push Service that acts like a central relay for messages. Each browser vendor runs their own Push Service, and it’s been designed to safeguard your privacy:
- To prevent cross-site correlations, every website receives a different, anonymous Web Push identifier for your browser.
- To thwart eavesdropping, payloads are encrypted to a public / private keypair held only by your browser.
- Firefox only connects to the Push Service if you have an active Web Push subscription. This could be to a website, or to a browser feature like Firefox Hello or Firefox Sync.
You’re always in control: Push notifications are opt-in, and you can revoke permission from any website at any time, either from the page info panel or from the “Notifications” section in Preferences → Content.
How does Web Push work?
Before today, people had to rely on apps, emails, or text messages for timely notifications. Now the Web can do that.
Web Push is an extension to the Service Worker standard, which means you can find excellent, annotated demos of Web Push in Mozilla’s Service Worker Cookbook. MDN also has great documentation on Web Push, and if you need to go straight to the source, the most recent Editor’s Draft of the specification lives on GitHub.
Last October, Chris Mills wrote an excellent introduction to Web Push here on Hacks, which explains the Service Worker lifecycle and how it relates to Push. To recap:
- A website registers a Service Worker with the browser. Service Workers are small JavaScript programs with super powers like intercepting network requests or running even when their parent website is closed.
- The Service Worker registration object exposes a pushManager property.
- The website uses the pushManager to either get an existing subscription or create a new one
- The subscription object exposes metadata about the subscription, including a unique endpoint URL on your browser vendor’s Push Service.
Whenever the website POSTs to that endpoint, the Push Service routes the message to your browser, where the appropriate Service Worker receives a push event. The Service Worker can then show a notification or take other actions.
The HTTP POST to the endpoint must include a “TTL” header, set to the number of seconds that the message should be held in case the user is not online. Once the TTL elapses, undelivered messages expire and will not be delivered. There is more technical information about this header on the Mozilla Services Blog.
In code, the Service Worker which receives the event might look like this:
self.addEventListener('push', function(event) {
event.waitUntil(
self.registration.showNotification('Example Notification', {
body: 'Hello, world!',
})
);
})
Meanwhile, registering the Service Worker and getting permission to show notifications might look a bit like the code below.
Note: This code sample uses ES7’s draft async/await syntax, since it reads most clearly. To use this in production, check out the equivalent in plain JavaScript.
async function registerForPush() {
// Register the Service Worker
let registration = await navigator.serviceWorker.register('service-worker.js');
// Check if we already have a subscription
let subscription = await registration.pushManager.getSubscription();
// If not, try to subscribe.
if (!subscription) {
subscription = await registration.pushManager.subscribe();
}
// Save the subscription data on our website's backend.
await fetch('/save-push-endpoint', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
// Done! Now our backend can send Push messages by POSTing to subscription.endpoint!
}
Again, there are a bunch of live demos with annotated source code over at the Service Worker Cookbook. If you’re confused, start by reading those.
Other Questions
Will a user’s endpoint URL ever change?
The endpoint can change at any time. In practice, this should be rare, but you should always be prepared to handle a pushsubscriptionchange event, and should check for a new endpoint whenever you getSubscription()
or subscribe()
.
What’s the state of browser support?
At the time of writing, Push works in Firefox for Desktop and has partial support in Chrome. Pushing to Chrome also requires some additional setup. The Microsoft Edge team lists Push as Under Consideration. More info at Can I Use?
How do I let a user unsubscribe from Push?
Use the subscription.unsubscribe() method. Don’t forget to also update your backend so you stop sending notifications to the old endpoint.
Can I check if subscribe() will prompt the user, before it actually happens?
Yep! Calling pushManager.permissionState() returns a Promise that resolves to your current permission state: "granted"
, "denied"
, or "prompt"
.
Make sure to let us know what you think about the Push API in the comments. We welcome all feedback including suggestions, questions, and bug reports.
About Dan Callahan
Engineer with Mozilla Developer Relations, former Mozilla Persona developer.
54 comments