Skip to content

Manifest protocol_handlers

Published Updated

Per MDN, the protocol_handlers member “specifies an array of objects that are protocols which this web app can register and handle,” and “protocol handlers register the application in an OS’s application preferences; the registration associates a specific application with the given protocol scheme.” MDN’s own illustration: “when using the protocol handler mailto:// on a web page, registered email applications open.”

  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Android)No—mediumsource1
Chrome (Desktop)Yes96mediumsource—
Edge (Desktop)Yes96mediumsource—
Safari (iOS)No—mediumsource2
Safari (macOS)No—mediumsource3
Firefox (Desktop)No—mediumsource4
Samsung InternetNo—mediumsource5
  1. Desktop-only registration.
  2. Safari does not implement the `protocol_handlers` manifest member (MDN compatibility table, checked 2026-10-03).
  3. Safari does not implement the `protocol_handlers` manifest member (MDN compatibility table, checked 2026-10-03).
  4. Desktop Firefox does not install web apps from the manifest and does not implement `protocol_handlers` (MDN compatibility table, checked 2026-10-03).
  5. Chromium ships manifest protocol handlers for desktop installs only; Samsung Internet lists no support (MDN compatibility table, checked 2026-10-03).

Source data: /compatibility/protocol-handlers.json · Global usage: 37 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-06-24 · Confidence: medium (computed from sources)

MDN marks the member experimental. Its browser-compat-data entry for manifests.webapp.protocol_handlers records support in Chrome 96, with Edge and Opera listed as mirroring Chrome’s data. Firefox and Safari are recorded with no support, as is Chrome on Android; the remaining mobile entries (Firefox for Android, Opera Android, Safari on iOS, Samsung Internet, and the Android and iOS WebViews) are recorded as mirrors of those same engines.

Each entry pairs a protocol with a url. Per MDN, protocol is “a required string containing the protocol to be handled; e.g.: mailto, ms-word, web+jngl,” and url is a “required HTTPS URL within the application scope that will handle the protocol,” where “the %s token will be replaced by the URL starting with the protocol handler’s scheme.” MDN adds that “if url is a relative URL, the base URL will be the URL of the manifest.”

MDN’s example declares that the app “should be registered to handle the protocols web+jngl and web+jnglstore”:

{
"protocol_handlers": [
{
"protocol": "web+jngl",
"url": "/lookup?type=%s"
},
{
"protocol": "web+jnglstore",
"url": "/shop?for=%s"
}
]
}

MDN describes what happens next: “after registering a web app as a protocol handler, when a user clicks on a hyperlink with a specific scheme such as mailto:// or web+music:// from a browser or native app, the registered PWA would open and receive the URL.”

The Manifest Incubations specification splits that into two actors. On installation, “a user agent SHOULD register protocol handlers with the Operating System,” and from then on “clicking on a registered protocol will launch the registered application. If this is the web app, then execute the [invoke a protocol handler] steps defined in [HTML], where the user agent SHOULD navigate to url and the appropriate browsing context is set to a new top level browsing context.” Where several apps claim the same protocol, the spec expects that “the OS allows the user to select which app should open it, and also allows the user to set a default app.”

The spec’s own worked example makes the substitution concrete: for a manifest at https://example.com/manifest.webmanifest, a web+music handler with "url": "/play?songId=%s" activated on web+music://#1234 means “the user agent would instantiate a new top-level browsing context and navigate to https://example.com/play?songId=web+music://%231234.”

How to detect the substituted URL and fall back

Section titled “How to detect the substituted URL and fall back”

Per MDN, %s is replaced with “the URL starting with the protocol handler’s scheme.” The cited sources do not define the unsubstituted template as a fallback URL, so the route should accept only the documented substituted shape and use its fallback branch for an absent or invalid value:

// The route the manifest points at: /lookup?type=%s
//
// Note the parser. MDN: "The URLSearchParams constructor interprets plus signs
// (+) as spaces, which might cause problems." In the spec's worked example the
// "+" of "web+music" is literal in the query string, so read the raw value.
const encoded = location.search.match(/[?&]type=([^&]*)/)?.[1];
let value = null;
try {
value = encoded ? decodeURIComponent(encoded) : null;
} catch {
// An unsubstituted or malformed value is not a handled protocol URL.
}
if (value?.startsWith('web+jngl:')) {
// The value has the shape MDN documents for a substituted %s.
showLookupFor(value.slice('web+jngl:'.length));
} else {
// Absent, or not a web+jngl URL. Render the normal lookup page with its own
// search field instead of parsing a value that isn't there.
showLookupForm();
}
  • Keep the handler url inside the app’s scope and on HTTPS, per MDN. The spec’s processing steps skip any entry whose normalized URL “is not within scope of” the manifest, and also skip entries where protocol or url is undefined.
  • Do not expect an arbitrary scheme to be accepted. In the spec’s example, the second handler “would be ignored, as the protocol provided does not start with web+ and is not part of the safelisted schemes.”
  • Do not register the same URL twice: per the spec’s processing steps, if the processed list already contains the normalized URL, the entry is skipped.
  • A permission prompt is not guaranteed, and the presented list may be shortened. Per the spec, “a user agent SHOULD ask users for permission before registering a protocol handler description protocol_handlers as the default handler for a protocol with the host operating system,” and it “MAY truncate the list … in order to remain consistent with the conventions or limitations of the host operating system.”
  • Treat registration as operating-system dependent, and not only an install-time act. Per MDN, “registering applications to handle URL schemes is operating-system dependent. This association is usually done during application install but it can also be done afterwards from an app that has already been installed.”
  • Parse the whole URL, not a payload. %s is replaced “by the URL starting with the protocol handler’s scheme” (MDN) — in the spec’s example the query value is the entire web+music://#1234, with the fragment marker written as %23.
  • Be aware the registration can become a default silently. The spec’s privacy note warns that “depending on the operating system capabilities, the protocol handler could become a ‘default’ handler … of a given protocol without the explicit knowledge of the user,” and lists the protections a user agent may employ, including removing the registration.