[Proposal] URL Protocol Handler Registration for PWAs

It is very common for native apps to register themselves as protocol (i.e.: scheme) handlers. Web developers should similarly be able to register PWAs to handle custom URL schemes (e.g.: mailto://, ms-word:// or web+music://). Arguably, the best time for this registration to happen is when the app is installed, via manifest options.

In this scenario, after having a PWA registered as a protocol handler, when a navigation to a URL with the registered protocol happens, the PWA should open and handle that URL. More details can be found on the explainer available here.


It seems that Google demonstrated some interest here. @mgiuca can you confirm?


I am personally supportive of this but I can’t speak on behalf of Google (at least, there is no internal consensus yet). We are discussing internally and I haven’t heard any objections.

I’d certainly be OK with creation of a WICG repo to explore. On the other hand, on the spec side, the feature is relatively small, so it might not even be necessary to do so. We could potentially discuss on the bug and just write it into the spec as-is.

Precedent is the shortcuts feature proposed by Aaron Gustafson, which I believe is more complex than protocol_handlers.

On Android at least, native apps can also register themselves as handles for domains.

Some web developer feedback: We experimented with URL handler protocol registrations for a while with Mastodon (https://joinmastodon.org), but we ran into several UX challenges on the way and ultimately had to remove the feature entirely. The biggest problems were:

  • The registration flow is very confusing for a non-technical end user. Browser restrictions on this feature make it almost invisible, and messaging around this feature is often overly technical—users shouldn’t need to understand what protocol handlers and web+mastodon:​// is to use a share button. I think this proposal, tying it to PWA installation, makes a lot of sense and would help a lot in this area.

  • There’s no way for users to choose between multiple custom protocol handlers. Right now, the browser implementation of custom protocol handlers allows for only one registered website per protocol handler—any additional registrations override the first one. Not only is this inflexible, it means that browser have to be more restrictive and present more permission prompts to the users ahead of time, before a link is ever used at all. This increases user confusion and permission fatigue. The explainer mentions this as a valid use-case, but I think it’s important to acknowledge that we would have to overhaul most current browser implementations of registerProtocolHandler to support it, and we may also want to make sure to call it out specifically when writing the spec text. I would also hate to get into a situation where PWA apps have good protocol handler UX but non-PWA sites have avoidably bad UX because of a historical accident.

  • There’s no good fallback or feature detection story. If someone includes a web+mastodon link in their HTML content, and the user hasn’t heard of mastodon or managed to install any protocol handlers (see the first bullet :D​) they just get a broken link. Most native platforms allow for deeplinks to have some sort of fallback to an app store or similar, it would be great if we had a similar sort of fallback mechanism for protocol handlers. (There are workarounds here using Javascript and a complicated XHR dance, but they’re kind of fragile and increase complexity). Of course, care needs to be taken to avoid this being an avenue for passive fingerprinting.

to give a little more insight into our main use-cases for this feature in Mastodon: Mastodon is a specific client software for the ActivityPub distributed social network. Since there are many thousands of Mastodon instances run by many different people, a user might be reading a post on one instance, like https:​//cybre.space, and then want to interact with it from their account on https:​//snouts.online. So we used custom protocol handlers to link the “like” and “boost” buttons on one status to a URI schema the web interface could pick up on. You can read a little bit more about the broader interest in this kind of feature from the Social Web community group (which specs ActivityPub) here: https://socialhub.activitypub.rocks/t/suggestion-a-custom-url-scheme-handler-as-a-streamlined-ux-for-cross-instance-interactions-on-the-web/504

1 Like

Now, taking off my developer hat and putting on my spec hat, it seems from the explainer that there are three broad use-cases for this feature, some of which overlap with other technologies:

  1. Linking from one app to a specific, different app. This is the use-case that traditionally supports skype://, ms-word:​//, etc. The vast majority of examples that the doc gives fall into this category. IMO, these work just as well as normal links that fall into the scope of the web app. Except for backwards compatibility, there’s no reason these need to be custom protocol handlers at all, and I don’t think anything is gained by making them one. For example, if a native or web application wanted to send you to a specific track on Spotify, they could link you to https://open.spotify.com/track/5VZZp4pnzogH7xboEv1c3r, and, if Spotify was a PWA, the user agent would already open the link in Spotify. For progressive enhancement, fallback and general compatibility reasons, this seems vastly preferable to spotify:track:5VZZp4pnzogH7xboEv1c3r

  2. Supporting a specific, non-web protocol that has an independent, standardized URI scheme. For example, magnet, ftp or dat. These are independent URI schemes that are co-equal to http, and generally “make sense” outside of a application-launching context. For example, you might cite files that are available on dat, or bookmark a FTP directory to come back to it again. Just like native apps, we should support parity by allowing PWAs to register handlers and open for these.

  3. Triggering a specific action that could be handled by one of many apps. For example, mailto, or the web+music example given in the specification. These actions tend to be much less formally standardized, and they don’t make a ton of sense outside of the navigation process. This seems like it is basically the use-case Web Intents/Activities was designed to support—I’m not familiar with any of the history there, but it looks like it had interop issues and didn’t get a lot of traction. However, lots of platforms (but not all) use a simpler custom URI scheme methodology for handling this, the most famous of which is mailto:.

(Writing that up has made me realize another use-case exists which isn’t well-served by any existing technology, which is interoperability with existing system activity/Intent frameworks that don’t primarily use custom protocols, like Android’s Intent framework or iOS’s SiriKit. It would be very useful to allow a PWA to respond to, for example, asking a voice assistant to create a new todo item. However, that use-case is entirely outside of the scope of this proposal)

Given the use-cases above, I think it would be best if the explainer for this feature refocuses on #2 and #3, and avoids the complications and inherent questions that may come with #1, which are already well-served by existing web platform technologies (except for backcompat/interop).

@nightpool, thank you for your insightful comments.

To address your feedback given from the perspective of a web developer:

We have also seen issues with registerProtocolHandler including:

  1. Only allowing one registration per handler
  2. Calling unregisterProtocolHandler deregisters the protocol only with the browser, not the OS
  3. Prompting the user to accept/deny something that they aren’t particularly knowledgeable about

This behavior isn’t part of the spec (https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers), so it could be changed. However, it would be outside the scope of this proposal to suggest UX fixes for the registerProtocolHandler API.

I agree that there is no link fallback story if a user is accessing the link from a browser. However, protocols are an OS level concept with an OS level fallback story. I think the best we can do is allow the OS to fallback when accessing protocols from native apps and pitch PWA protocol handling for what it is - a web hook into OS functionality.

To address your feedback from the perspective of a spec writer:

I agree with the divisions you’ve made among protocol types, however, your characterization of group 1 seems to downplay the importance of backwards compatibility with app-specific protocols. From a web-only perspective, I agree that app-specific links are less relevant and introduce compatibility issues. However, this proposal aims to support existing ecosystems of native applications, where protocols from group 1 are just as relevant as those in group 2 and 3.

I’ll also add that any native application can register to use any protocol, so a “proprietary” protocol introduced by a native app ecosystem can be handled by an arbitrary third-party application. One example is the Microsoft Office URI protocol (https://docs.microsoft.com/en-us/office/client-developer/office-uri-schemes) that is publicly documented, allowing other productivity software to handle a protocol that initially appears to app-specific. I use this example to point out that the lines between groups 1, 2, and 3 are blurred when you consider protocols as an OS feature meant to provide integration between native applications.

There seems to be some interest in this API and I would love to see some of the feedback to become issues we can follow up with. I’m requesting to move the spec to a WICG repo. @yoavweiss, can you help us with that?

I’d certainly be OK with creation of a WICG repo to explore. On the other hand, on the spec side, the feature is relatively small, so it might not even be necessary to do so. We could potentially discuss on the bug and just write it into the spec as-is.

I’m fine with us moving this discussion to the issue above in the manifest repo. Indeed, most of the things being discussed here are implementation details and the spec side of this work shouldn’t warrant a dedicated repo. Is anyone opposed to the idea?