Web Push Arrives in Firefox 44

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.

Screenshot of a Push Notification on Mac OS X

Much like with geolocation or webcam access, Web Push requires explicit, revokable permission before a website can show notifications to a user.

Screenshot of the in-browser Push Notification permissions prompt

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:

  1. To prevent cross-site correlations, every website receives a different, anonymous Web Push identifier for your browser.
  2. To thwart eavesdropping, payloads are encrypted to a public / private keypair held only by your browser.
  3. 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:

  1. 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.
  2. The Service Worker registration object exposes a pushManager property.
  3. The website uses the pushManager to either get an existing subscription or create a new one
  4. 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.

More articles by Dan Callahan…


54 comments

  1. Minishlink

    Good news!

    For the server side of Web Push, you have several libraries that already exist:
    – web-push from Marco Castelluccio for Node.js: https://github.com/marco-c/web-push
    – WebPush from me (Louis Lagrange) for PHP: https://github.com/Minishlink/web-push

    I am not aware of any implementation in Python.

    January 26th, 2016 at 08:50

  2. Valentin

    I wonder… What are the requirements for the Push Server ? Isn’t it a (extremely) huge database ? And as a user, can I choose another push server ?

    January 26th, 2016 at 09:18

    1. Dan Callahan

      Our production Push Server is written in PyPy, and can be found at https://github.com/mozilla-services/autopush. From what I recall, the main concern is memory overhead per connection, since the service has to maintain an enormous number of websocket connections, the majority of which are idle at any given time.

      Self-hosting isn’t necessarily supported, but it’s not impossible. Once you have another service up and running, you’d just have to change dom.push.serverURL in about:config.

      January 26th, 2016 at 09:57

  3. Jesus Perales

    This implementation is compatible with web push for Firefox OS ?

    January 26th, 2016 at 10:09

  4. g

    I do suppose there will be a configuration item to unconditionally turn this crap off, right?

    January 26th, 2016 at 12:54

    1. Dan Callahan

      Right now, user-visible settings are on a per-origin basis. E.g., you can dismiss prompts with “never ask me again [on this site].” If you want to turn Push off altogether, toggle dom.push.enabled in about:config.

      January 26th, 2016 at 13:16

  5. Bob

    I guess Netscape just couldn’t stand to let Netscape Netcaster rest in peace.

    January 26th, 2016 at 13:40

  6. David Piepgrass

    I was shocked by how this feature behaved in Chrome, and users of Firefox are in for a similar surprise. The problem is that there is NO INDICATION that you can receive “notifications” from a web site that isn’t open in your browser.

    Sure, when the Facebook tab was open it asked me if I wanted “notifications” and I said “sure!” It never crossed my mind that I’d still get “notifications” AFTER I CLOSED THE TAB.

    WHY doesn’t Firefox mention that notifications will be received even when the site is closed? Will notifications still be received when ALL tabs are closed? Don’t give ME the answer: change the UI and give USERS the answer.

    January 26th, 2016 at 16:58

  7. epk

    “Have you ever wished that a website could notify you when something important happened, even if you didn’t have the site open?”

    Uggh, no, never. Nu-uh.

    But I do wish for a browser which continues to embrace dynamic functionality extension via XUL and XPCOM. And for a serious investment of developer time in Thunderbird and its underlying core. How about that?

    January 26th, 2016 at 17:07

  8. Peter

    Please have a separate setting for whether I can receive notifications while a tab is loaded vs notifications received after a tab is closed. These seem like very different situations, and as a user I don’t appreciate my giving approval for a loaded tab to notify me of updates meaning that they can send me a message whenever regardless of if they are loaded anywhere.

    January 26th, 2016 at 20:02

  9. Benoit

    Is there a bug for an implementation of this in Firefox for Android? It could allow users to uninstall dozens of apps that are just useful for their notifications (Facebook in particular).

    January 27th, 2016 at 00:30

    1. Dan Callahan

      Bug 1206207 is the meta-bug for Push support on Firefox for Android. The dependency tree is looking pretty promising; it’s just taking a bit longer than on Desktop since we need to tunnel everything through Google Cloud Messaging on Android, instead of connecting the browser directly to our own push service.

      January 27th, 2016 at 06:15

  10. PhistucK

    I agree with David Piepgrass and Peter.

    Also, when a message is pushed to the browser, is the service worker required to show a notification?
    Can it just update some stale data that exists in the cache API instead, for example?
    If so, this means that allowing notifications opens up a can of bandwidth worms to the user without consent…

    January 27th, 2016 at 04:39

    1. Dan Callahan

      The Service Worker isn’t required to show a notification, but messages that don’t show user-visible notifications are subject to quotas / throttling. There’s a demo of that in the Service Worker Cookbook.

      For folks that need to update caches, we’re working on supporting the Background Sync API.

      January 27th, 2016 at 06:36

  11. don

    is there a centralized place in Firefox where I can see which sites I have granted permission to show me notifications? so I can disable/enable them at will?

    January 27th, 2016 at 04:42

    1. Dan Callahan

      Yep! Check out the “Notifications” section in about:preferences#content

      January 27th, 2016 at 05:56

  12. Игорь Петренко

    Hello!

    How to organize the notification tumlbr.com | blogspot.com ?? Maybe there is some kind of service?

    January 27th, 2016 at 05:36

    1. Dan Callahan

      Each site must program their own support for Push. Someone could create a general RSS -> Push service, but I don’t think any exist yet.

      January 27th, 2016 at 06:09

  13. Andris

    I agreee with Peter on this one.
    It is very important to distinguish these 2 situations and implement them in settings.

    January 27th, 2016 at 05:50

  14. Steffen

    What should my server do when it receives an HTTP 404 error with the following body in response to POSTing to an endpoint URL?

    {“errno”:103,”code”:404,”error”:”Not Found”}

    According to https://github.com/mozilla-services/autopush/blob/master/docs/http.rst this means “Expired URL endpoint”. Is this error permanent and should I remove the affected endpoint URLs from my database?

    (Maybe “410 Gone” would be a more appropriate status code.)

    January 27th, 2016 at 06:31

    1. Dan Callahan

      Good point. I’ve filed autopush/issues/312 to suggest 410 Gone instead of 404 Not Found, since the correct action is to treat a 404 as a permanent failure, stop using that endpoint, and attempt to re-subscribe the next time the user visits your site.

      January 27th, 2016 at 06:48

  15. Odin Hørthe Omdal

    This is awesome. Thank you for working on this, and congrats on being the first browser to have this feature in the nice standardized form. Jumping through the extra hoops for Google’s implementation is a bother. This is so much nicer :)

    January 27th, 2016 at 06:43

  16. mattip

    This is cool. Even cooler for using PyPy (I am a contributor :) ). Could we get PyPy added to the tools Mozilla is using?
    https://wiki.mozilla.org/MOSS/Projects_in_use_by_Mozilla

    January 27th, 2016 at 11:41

    1. Dan Callahan

      Done!

      January 27th, 2016 at 13:00

  17. PushEngage

    Great news Dan and Firefox team. We have been waiting for this.

    We are a platform to manage Push Notifications across Browser, and are working to integrate Firefox immediately.

    January 28th, 2016 at 02:48

  18. Mahavir

    Is there any chances of common API for Firefox, Chrome and Safari in near future?

    January 28th, 2016 at 10:27

  19. PhistucK

    @Mahavir –
    See https://groups.google.com/a/chromium.org/d/msg/blink-dev/_LQX_cjAATA/blFj3FBVBQAJ – Google is working on the Web Push Protocol as well.

    January 28th, 2016 at 10:52

  20. abc

    This doesn’t seem to be in the regular FF44 release notes (https://www.mozilla.org/en-US/firefox/44.0/releasenotes/), nor in the developer ones (https://hacks.mozilla.org/2015/11/developer-edition-44-creative-tools-and-more/).

    January 28th, 2016 at 13:50

    1. Dan Callahan

      Whoops! I’ve filed Bug 1233762 to resolve that.

      February 1st, 2016 at 08:34

  21. Tim

    The gear menu on the notification is practically useless. A notification pops up, you move the mouse to the gear and click it and the menu is displayed, then the whole thing disappears before you can even read the menu because the notification period ends, completely ignoring the fact that the user is interacting with it. The notification should be more intelligent about whether the mouse is over it, and especially if the menu is being displayed.

    January 28th, 2016 at 20:51

    1. Dan Callahan

      Good catch. I’ve filed that as Bug 1244102.

      January 29th, 2016 at 06:07

  22. gialloporpora

    I have a question about this:
    “To thwart eavesdropping, payloads are encrypted to a public / private keypair held only by your browser.”
    What is the algorithm used to cypher data?

    January 29th, 2016 at 05:32

    1. Dan Callahan

      The specifics are still subject to change, but right now subscriptions in Firefox 44 support getKey('p256dh'), which is an ECDH public key over curve P-256. Details on its use in the IETF draft of Web Push Encryption.

      January 29th, 2016 at 06:32

  23. angry user

    On linux these notifications do not appear to work properly:
    – the cog does not display, instead an ugly dropdown menu does.
    – ‘x’ (close) button does not work at all.

    other issues:
    – changing the time to 12 secs instead of 4??? why? If I wanted to read it that bad I would go look at the page…
    – No options at all to customize appearance, delay, etc…

    January 29th, 2016 at 07:29

  24. gialloporpora

    Dan, thanks a lot for your
    clarification about encryption.

    January 29th, 2016 at 14:36

  25. Tim

    Thanks for taking my bug report. :) I would have replied to your reply, but I don’t see a way. :-/

    January 30th, 2016 at 14:47

    1. Dan Callahan

      That’s potch/hax/issues/22. Patches welcome. ;-)

      January 30th, 2016 at 16:30

  26. PhistucK

    > The Service Worker isn’t required to show a notification, but messages that don’t show user-visible notifications are subject to quotas / throttling. There’s a demo of that in the Service Worker Cookbook.

    I clicked on the button twice and I got 94 invisible notifications. This is a lot. The service worker can download tons of data (depending on your connection speed)…

    Is the quota per day? Per month? Per hour? Per user?
    Also, I see that after I show a visible notification, the invisible notification quota resets itself or something.

    This seems harmful. Allowing notifications should not mean allowing data to be exchanged when the tab is closed. This needs a separate permission, in my opinion.

    February 1st, 2016 at 22:53

    1. Dan Callahan

      We’re still tweaking the exact formula, but reading through Bug 1153504 is a good place to get started. :)

      To quote Kit Cambridge today, “We changed it a bit. You now have 3 seconds after a push comes in to show a notification. If you show at least one, we won’t dock you. If you’re quick enough, you can send lots of background updates in those first 3 seconds, and then your subscription gets dropped.”

      February 2nd, 2016 at 08:49

  27. dst

    Would it be possible to (optionally) have Firefox Android also use Mozilla’s server directly (or self-hosted) for those of us using android without Google apps? If I wanted a mobile browser that shares as much of my data as possible with Google, I’d be using chrome ^^

    February 2nd, 2016 at 15:05

  28. Richard

    Can anyone please tell me what maybe wrong with this test push?

    curl -X PUT -d “version=5” –insecure https://updates.push.services.mozilla.com/update/gAAAAABWs….pQ=

    The doco says encryption is not required without payload and the full endpoint does match.

    PS. This works for Chrome: –
    curl –insecure –header “Authorization: key=AIzaSyC….XZyJGmq6M” –header Content-Type:”application/json” https://android.googleapis.com/gcm/send -d “{\”registration_ids\”:[\”erEmvJgHoAQ:AP….bSKl\”]}”

    February 4th, 2016 at 20:25

  29. Kit Cambridge

    PhistucK: Excellent question. :-) Elaborating a bit more…

    The quota is a curve, based on the number of days since you last visited the site. A site is allowed 16 background messages for the first day, 8 for the second, 5 for the third, and so on. For each `push` event, a service worker has 3 seconds to show a notification. This lets a site update an existing notification using the tag.

    As you noticed, it’s possible to send lots of background messages within those first 3 seconds. After that, the subscription is removed, and will only be reinstated within 24 hours of your next visit. This should encourage sites to do the right thing.

    We don’t want developers relying on the exact formula, so that’s why we’ve been vague about it. However, this makes it opaque for folks who are (quite understandably) concerned about background activities. We could add an entry to the FAQ page about this.

    Does that help?

    February 4th, 2016 at 21:21

  30. PhistucK

    No, it does not help. People opt in for notifications and their data may be wasted. Sorry, this throttling does not solve the bad explanation to the user and the unexpected outcome.

    February 5th, 2016 at 05:55

  31. PhistucK

    > That’s potch/hax/issues/22. Patches welcome. ;-)
    Not so welcome, I guess. :(
    https://github.com/potch/hax/pull/31

    February 5th, 2016 at 12:20

    1. Dan Callahan

      Sorry about that! I asked a teammate to review that on Monday, but he’s been swamped. I’ll see what I can do to get that merged. Thank you so much for submitting that! :)

      February 5th, 2016 at 13:15

  32. Richard Maher

    Please skip my question above as it was a simple case of me using PUT instead of POST.

    A more pressing question is can someone please provide a C# and/or JAVA example of a server encrypting and then POSTING a payload laden message to Firefox 44?

    Here is my question to the C# forum: –
    https://forums.asp.net/t/2084499.aspx?C+server+PUSHing+an+encrypted+message+to+Firefox+44+example

    February 7th, 2016 at 17:32

  33. narendra

    This looks great! I have few questions regarding endpoint subscribe/unsubscribe.

    – What is the lifetime of the endpoint? Is it unique to user session, or browser/machine/ip combination?
    – When endpoint expire, how is that communicated to the service backend? Service backend will need to remove the stale/expired endpoints for the user. Would 404 error code during message send be good place for this usecase? https://github.com/mozilla-services/autopush/blob/master/docs/http.rst#id7 ment

    February 8th, 2016 at 16:17

    1. Dan Callahan

      The endpoint should basically exist until the user revokes permission, which would manifest as a 4xx response code. The endpoint is unique to each browser profile. E.g., even if you’re running both Developer Edition and Normal Firefox on the same machine, connected by Firefox Sync, each instance would create and maintain its own unique endpoints.

      February 8th, 2016 at 16:52

  34. narendra

    @Dan ‘The endpoint is unique to each browser profile’

    Does that mean the endpoint is not specific to a user? If user1 logs in my webapp, the endpoint is created and sent to the service backend where I store it for user1. Now if user1 logs out in my webapp on the same browser and user2 logs in, does he get the same endpoint? The notifications meant for user1 would be sent to user2 right.

    What am i missing here?

    February 8th, 2016 at 18:00

    1. Dan Callahan

      Good point. Your site would get the same endpoint for both users, since it’s the same browser.

      You would get a different endpoint if the browser reset itself between users, like with a properly configured public terminal, or if the users had different local accounts on the machine.

      February 8th, 2016 at 19:23

  35. Me

    Is there a way to disable ‘globally’ this feature?

    Everytime I enter in a website with this feature, firefox asks me if I want notifications…

    February 14th, 2016 at 19:05

  36. Marco Colli

    I have a website that sends push notifications with the Push API. Everything worked fine, however now I am experiencing the following issue when my app POST the notification to Mozilla servers:

    Response 400 Bad Request: {“errno”: 111, “message”: “Missing TTL header”, “code”: 400, “error”: “Bad Request”}

    What does that mean? Are you referring to HTTP or to the notification TTL? Please help or provide documentation

    February 19th, 2016 at 05:56

    1. Dan Callahan

      I’m terribly sorry about that — the spec recently changed to require an explicit timeout for messages, and we deployed a new version of the Push service without adequate notice or backwards compatibility. I’m updating the article presently.

      February 19th, 2016 at 22:25

  37. Marco Colli

    Can you please provide documentation about response messages and codes (and errno) that we should expect from Mozilla push servers?

    For example some endpoints produce the following error:
    404 Not Found: {“errno”: 106, “message”: “No such subscription”, “code”: 404, “error”: “Not Found”}

    I think that the endpoint should be permanently removed from our servers since that subscription has expired. However I have some doubts:
    – any 404 Not found means that the subscription has permanently expired?
    – should we also check the errno (106)? Or maybe the message?

    The fear is to remove endpoints that return error status codes only temporarily.
    Thank you very much!

    February 20th, 2016 at 07:24

Comments are closed for this article.