Starting in Edge 77 (and Chrome 77), the prompt shown when launching an AppProtocol from the browser was changed to remove the “Always allow” checkbox. That change was made, in large part, because this prompt is the only thing standing between every arbitrary site on the Internet (loaded inside your browser’s sandbox) and a full-trust application on your computer (running outside of the browser’s sandbox). See the prior blog post for details on why AppProtocols are so scary.

After Edge 77, when you try to launch a Microsoft Teams meeting, for instance, you’ll see a UI like this:

Unfortunately, there’s a downside to this security improvement.

The same prompt that protects users from malicious content on https://BadGuy.example also shows every single time the legitimate Microsoft Teams website tries to open its related application. Users complain that the security prompt feels redundant, and IT departments have howled that they’ll have to retrain users and field helpdesk calls.

Starting in Edge 82.0.425.0 Canary, a new flag is available:

Visit edge://flags/#edge-exclude-schemes-per-origin, set the flag to Enabled, and restart the browser. After doing so, you’ll see that the prompt now includes a new checkbox: “Always allow <hostname> to open links of this type in the associated app”:

By storing exemptions on a per-site, per-scheme basis, attack surface is significantly reduced, because only sites you’ve specifically allowed in the past are permitted to bypass the prompt.

This change will also be available in browsers based on Chromium 84.

Some notes on this change:

  • Exemptions are stored on a per-scheme, per-origin basis (e.g. “Allow teams: from“, so if multiple origins use the same scheme, you’ll need to exempt each one.
  • Stored exemptions are origin specific: “https://site.example&#8221; and “” and “http://site.example” are all different origins.
  • Stored exemptions are only available for secure origins (basically, HTTPS, HTTP-to-Localhost, and FILE).
  • This checkbox is visible by default in Edge 84, but can be disabled using the existing Group Policy.
  • At present, there is no Group Policy for an admin to push exemption pairs to the client. We are investigating this request.
  • To clear stored exemptions, you may continue to use the “Cookies and other site data” checkbox in the Clear Browsing Data dialog box. Note that you can set the time range to anything you like– all Origin+Scheme exemptions will be cleared.

You can experiment with this feature using the AppProtocol test page.


I’ve previously written about Web-to-App communication via Application Protocols. App Protocols allow web content to invoke a native application outside of the browser.

WebApp advocates (like me!) want to continue to close the native/browser gaps that prevent web applications from becoming full-fledged replacements for native apps. To that end, I’ve recently spent some time looking at how the web platform allows JavaScript registration of a protocol handler, where the handling “app” is a same-origin web page.

Currently supported by Firefox and Chromium-based browsers (on platforms other than Android), the function navigator.registerProtocolHandler(scheme, url_template, description) enables a website to become a handler for a URL scheme.

Real-World Usage

The canonical use-case for this is web based email clients like Gmail. Gmail would like to be able to be the user’s handler for mailto links. When the user clicks a mailto link, the content of the link should be sent to a handler page on which responds accordingly (e.g. by creating a new email to the specified addressee).

The registerProtocolHandler API isn’t limited to the mailto scheme, however. It presently supports a short list of allowed schemes1, and any scheme named web+{one-or-more-lowercaseASCII}.

User Experience

I’ve built a page containing a number of test cases here. When you push the button to register a protocol handler, you receive a permission prompt from Chrome/Edge or Firefox:



To avoid annoying users, if the user declines Chrome’s prompt, the site is blocked from re-requesting permission to handle the protocol. A user must manually visit the settings page to unblock permission.

User-Gesture Requirements

If a page attempts to call registerProtocolHandler() on load or before the user has interacted with the page (a so called “gesture”), then Chromium-based browsers will not pop the permission prompt. Instead, an overlapping-diamonds icon is shown at the right-hand side of the address bar, with the text “This page wants to install a service handler.” Here’s what this looks like on Gmail:



Within Chrome, you can view your currently registered handlers (and sites blocked from asking to become the registered handler) by visiting chrome://settings/content/handlers.


Operating System Integration

One particularly interesting aspect of allowing web-based registration of protocol handlers is that it is desirable for the rest of the operating system outside of the browser to respect those protocol handler associations.

For example, clicking a mailto link in some other application should launch the browser to the web-based handler if registered. However, having the browser change system state in this manner is complicated, especially on modern versions of Windows whereby various protections have been put in place to try to prevent “hijacking” of file type and protocol handler associations.

Edge and Chrome will only attempt to become the systemwide handler for a protocol when running a channel that offers to become the default browser (e.g. Stable). On such a channel, if the browser wasn’t already the handler for the protocol, after the user clicks “Allow” on the Chrome permission bubble, a Windows UAC dialog is shown:


If the user accepts by clicking “Yes”, the RegisterChromeForProtocol function silently updates the registry:


Other Things I’ve Learned

  • Chrome, Edge, and Firefox disallow registration of protocol handlers in Private/Incognito/InPrivate modes.
  • With my patch landed, Chrome, Edge, and Firefox disallow registration of protocol handlers from non-secure contexts (e.g. HTTP). Due to the same-origin requirement for the handler URL, this effectively prevents the use of a non-secure page as a handler.
  • An upcoming patch proposes blocking registration from cross-origin subframes.
  • Chromium-based browsers enable IT admins to set default scheme-to-web mappings using Group Policy.
  • Chrome presently fails to enforce specified limits on web+ protocol names. Firefox does enforce the limits.
  • Firefox does not support targeting a RPH registered protocol as the target of a form POST request; it silently drops the POST body.
  • Firefox does not implement the unregisterProtocolHandler API. Users must manually unregister protocol handlers using the browser UI.
  • On Windows at least, neither Firefox Stable nor Firefox Nightly seems to try to become the systemwide handler for a scheme.
  • If you have a custom scheme link in a subframe, you probably want to add a target=_blank attribute on it. Otherwise, your protocol handler web page might navigate within that subframe and get blocked due to privacy settings or X-Frame-Options directives


1 The permitted schemes are bitcoin, geo, im, irc, ircs, magnet, mailto, mms, news, nntp, openpgp4fpr, sip, sms, smsto, ssh, tel, urn, webcal, wtai, xmpp.

Type in your web browser’s address bar and hit enter.

What happens?

Before connecting to the server, your browser must convert “” to the network address at which that server is located.


It does this lookup using a protocol called “DNS.” Today, most DNS transactions are conducted in plaintext (not encrypted) by sending UDP messages to the DNS resolver your computer is configured to use.

There are a number of problems with the 36-year-old DNS protocol, but a key one is that the unencrypted use of UDP traffic means that network intermediaries can see (and potentially modify) your lookups, such that attackers can know where you’re browsing, and potentially even direct your traffic to some other server.

The DNS-over-HTTPS (DoH) protocol attempts to address some of these problems by sending DNS traffic over a HTTPS connection to the DNS resolver. The encryption (TLS/QUIC) of the connection helps prevent network intermediaries from knowing what addresses your browser is looking up– your queries are private between your PC and the DNS resolver that is providing the answers. The expressiveness of HTTP (with request and response headers) provides interesting options for future extensibility, and the modern HTTP2 and HTTP3 protocols aim to provide high-performance and parallel transactions with a single connection.

Try It

Support for DNS-over-HTTPS is coming to many browsers and operating systems (including a future version of Windows). You can even try DoH out in the newest version of Microsoft Edge (v79+) by starting the browser with a special command line flag. The following command line will start the browser and instruct it to perform DNS lookups using the Cloudflare DoH server:

msedge.exe --enable-features="DnsOverHttps<DoHTrial" --force-fieldtrials="DoHTrial/Group1" --force-fieldtrial-params="DoHTrial.Group1:Fallback/false/Templates/"

You can test to see whether the feature is working as expected by visiting Unfortunately, this command line flag presently only works on unmanaged PCs, meaning it doesn’t do anything from PCs that are joined to a Windows domain.

Some Thoughts, In No Particular Order

Long-time readers of this blog know that I want to “HTTPS ALL THE THINGS” and DNS is no exception. Unfortunately, as with most protocol transitions, this turns out to be very very complicated.


The privacy benefits of DNS-over-HTTPS are predicated on the idea that a network observer, blinded from your DNS lookups by encryption, will not be able to see where you’re browsing.

Unfortunately, network observers, by definition, can observe your traffic, even if the traffic encrypted.

The network observer will still see the IP addresses you’re connecting to, and that’s often sufficient to know what sites you’re browsing.

Worse, they are usually still able to tell what specific HTTPS site you’re visiting on that IP address. That’s because one of the current limitations of HTTPS is that the browser sends, in unencrypted form a Server Name Indication (SNI), the hostname it expects to see in the server’s certificate as a part of the ClientHello HTTPS handshake message. Closing this hole requires implementation of Encrypted SNI (ESNI) and this feature is not yet implemented in Chromium.

Privacy From Observers, Not the Resolver

If your Internet Service Provider (say, for example, Comcast) is configured to offer DNS-over-HTTPS, and your browser uses their resolver, your network lookups are protected from observers on the local network, but not from the Comcast resolver.

Because the data handling practices of resolvers are often opaque, and because there are business incentives for resolvers to make use of lookup data (for advertising targeting or analytics revenue), it could be the case that the very actor you are trying to hide your traffic from (e.g. your ISP) is exactly the one holding the encryption key you’re using to encrypt the lookup traffic.

To address this, some users choose to send their traffic not to the default resolver their device is configured to use (typically provided by the ISP) but instead send the lookups to a “Public Resolver” provided by a third-party with a stronger privacy promise.

However, this introduces its own complexities.

Public Resolvers Don’t Know Private Addresses

A key problem in the deployment of DNS-over-HTTPS is that public resolvers (Google Public DNS, Cloudflare, Open DNS, etc) cannot know the addresses of servers that are within an intranet. If your browser attempts to look up a hostname on your intranet (say using the public resolver, the public resolver not only gets information about your internal network (e.g. now Google knows that you have a server called MySecretServer.intranet) but it also returns “Sorry, never heard of it.” At this point, your browser has to decide what to do next. It might fail entirely (“Sorry, site not found”) or it might “Fail open” and perform a plain UDP lookup using the system-configured resolver provided by e.g. your corporate network administrator.

This fallback means that a network attacker might simply block your DoH traffic such that you perform all of your queries in unprotected fashion. Not great.

Even alerting the user to such a problem is tricky: What could the browser even say that a human might understand? “Nerdy McNerdy Nerd Nerd Nerd Nerd Nerd Address Nerd Resolution Nerd Geek. Privacy. Network. Nerdery. Geekery. Continue?”

Centralization Isn’t Great

Centralizing DNS resolutions to the (relatively small) set of public DNS providers is contentious, at best. Some European jurisdictions are uncomfortable about the idea that their citizens’ DNS lookups might be sent to an American tech giant.

Some privacy-focused users are primarily worried about the internet giants (e.g. Google, Cloudflare) and are very nervous that the rise of DoH will result in browsers sending traffic to these resolvers by default. Google has said they won’t do that in Chrome, while Firefox is experimenting with using Cloudflare by default in some locales.

Content Filtering

Historically, DNS resolutions were a convenient choke point for schools, corporations, and parents to implement content filtering policies. By interfering with DNS lookups for sites that network users are forbidden to visit (e.g adult content, sites that put the user’s security at risk, or sites that might result in legal liability for the organization), these organizations were able to easily prevent non-savvy users from connecting to unwanted sites. Using DoH to a Public DNS provider bypasses these types of content filters, leaving the organization with unappealing choices: start using lower-granularity network interception (e.g. blocking by IP addresses), installing content-filters on the user’s devices directly, or attempting to block DoH resolvers entirely and forcing the user’s devices to fall back to the filtered resolver.

Geo CDNs and Other Tricks

In the past, DNS was one mechanism that a geographically distributed CDN could use to load-balance its traffic such that users get the “best” answers for their current locale. For instance, if the resolver was answering a query from a user in Australia, it might return a different server address than when resolving a query from a user in Florida.

These schemes and others get more complicated when the user isn’t using a local DNS resolver and is instead using a central public resolver, possibly provided by a competitor to the sites that the user is trying to visit.

Don’t Despair

Despite these challenges and others, DNS-over-HTTPS represents an improvement over the status quo, and as browser and OS engineering teams and standards bodies invest in addressing these problems, we can expect that deployment and use of DoH will grow more common in the coming years.

DoH will eventually be a part of a more private and secure web.

-Eric Lawrence

Support for the venerable FTP protocol is being removed from Chromium. Standardized in 1971, FTP is not a safe protocol for the modern internet. Its primary defect is lack of support for encryption (FTPS isn’t supported by any popular browsers), although poor support for authentication and other important features (download resumption, proxying) also have hampered the protocol over the years.

With removal first proposed by the networking lead nearly six years ago, FTP support has been gradually pared back, first blocking such urls for subresources in Chrome 59, and later forcing FTP resources to be treated as downloads in Chrome 72. Now FTP support be going away entirely, starting in version 80, although a flag (chrome://flags/#enable-ftp) will remain available to turn it back on for a limited time.

After FTP support is removed, clicking on a FTP link will either launch the operating system’s registered FTP handler (if any), or will silently fail to do anything (as Chrome fails silently when an application protocol handler is not installed).

If your scenario depends on FTP today, please switch over to HTTPS as soon as possible.




For security reasons, Edge 76+ and Chrome impose a number of restrictions on file:// URLs, including forbidding navigation to file:// URLs from non-file:// URLs.

If a browser user clicks on a file:// link on an https-delivered webpage, nothing visibly happens. If you open the the Developer Tools console, you’ll see a note: “Not allowed to load local resource: file://host/whatever”.

In contrast, Edge18 (like Internet Explorer before it) allowed pages in your Intranet Zone to navigate to URLs that use the file:// url protocol; only pages in the Internet Zone were blocked from such navigations1.

No option to disable this navigation blocking is available in Chrome or Edge 76+.

What’s the Risk?

The most obvious problem is that the way file:// retrieves content can result in privacy and security problems. Pulling remote resources over file:// can leak your user account information and a hash of your password to the remote site. What makes this extra horrific is that if you log into Windows using an MSA account, the bad guy gets both your global userinfo AND a hash he can try to crack.

Beyond the data leakage risks related to remote file retrieval, other vulnerabilities related to opening local files also exist. Navigating to a local file might result in that file opening in a handler application in a dangerous or unexpected way. The Same Origin Policy for file URLs is poorly defined and inconsistent across browsers, which can result in security problems.

Workaround: IE Mode

Enterprise administrators can configure sites that must navigate to file:// urls to open in IE mode. Like legacy IE itself, IE mode pages in the Intranet zone can navigate to file urls.

Workaround: Extensions

Unfortunately, the extension API chrome.webNavigation.onBeforeNavigate does not fire for file:// links that are blocked in HTTP/HTTPS pages, which makes working around this blocking this via an extension difficult.

One could write an extension that uses a Content Script to rewrite all file:// hyperlinks to an Application Protocol handler (e.g. file-after-prompt://) that will launch a confirmation dialog before opening the targeted URL via ShellExecute or explorer /select,”file://whatever”, but this would entail rewriting the extension rewriting every page which has non-zero performance implications. It also wouldn’t fix up any non-link file navigations (e.g. JavaScript that calls window.location.href=”file://whatever”).

Similarly, the Enable Local File Links extension simply adds a click event listener to every page loaded in the browser. If the listener observes the user clicking on a link to a file:// URL, it cancels the click and directs the extension’s background page to perform the navigation to the target URL, bypassing the security restriction by using the extension’s (privileged) ability to navigate to file URLs. But this extension will not help if the page attempts to use JavaScript to navigate to a file URI, and it exposes you to the security risks described above.

Necessary but not sufficient

Unfortunately, blocking file:// uris in the browser is a good start, but it’s not complete. There are myriad formats which have the ability to hit the network for file URIs, ranging from Office documents, to emails, to media files, to PDF, MHT, SCF files, etc, and most of them will do so without confirmation.

In an enterprise, the expectation is that the Organization will block outbound SMB at the firewall. When I was working for Chrome and reported this issue to Google’s internal security department, they assured me that this is what they did. I then proved that they were not, in fact, correctly blocking outbound SMB for all employees, and they spun up a response team to go fix their broken firewall rules. In a home environment, the user’s router may or may not block the outbound request.

Various policies are available, but I get the sense that they’re not broadly used.

Navigation Restrictions Aren’t All…

This post mostly covers navigating to file:// URLs, but another question which occasionally comes up is “how can I embed a subresource like an image or a script from a file:// URL into my HTTPS-served page.” This, you also cannot do, for similar security/privacy reasons. And that’s probably a good thing.

Chromium allows HTML pages served from file:// URIs to load images and scripts from the same path, but Legacy Edge (v18) and Internet Explorer are the only browsers that consider all local-PC file:// URIs to be same-origin, allowing such pages to refer to other HTML resources on the local computer. Other browsers treat file origins as unique, blocking DOM interactions between frames from different local files, etc.

Chromium’s Same-Origin-Policy treats file: URLs as unique origins, which means that if you open an XML file from a file: URL, any XSL referenced by the XML is not permitted to load and the page usually appears blank, with the only text in the Console ('file:' URLs are treated as unique security origins.) noting the source of the problem.

This behavior impacts scenarios like trying to open Visual Studio Activity Log XML files and the like. To workaround the limitation, you can either embed your XSL in the XML file as a data URL:

…or you can launch Microsoft Edge or Chrome using a command line argument that allows such access:

msedge.exe --allow-file-access-from-files


1 Interestingly, some alarmist researchers didn’t realize that file access was allowed only on a per-zone basis, and asserted that IE/Edge would directly leak your credentials from any Internet web page. This is not correct. It is further incorrect in old Edge (Spartan) because Internet-zone web pages run in Internet AppContainers, which lack the Enterprise Authentication permission, which means that they don’t even have your credentials.

Note: This post is part of a series about Web-to-App Communication techniques.

Just over eight years ago, I wrote my last blog post about App Protocols, a class of URL schemes that typically1 open another program on your computer instead of returning data to the web browser. 

App Protocols2 are both simple and powerful, allowing client app developers to easily enable the invocation of their apps from a website. For instance, ms-screenclip is a simple app protocol built into Windows 10 that kicks off the process of taking a screenshot:


When the user invokes this url, the handler waits two seconds, then launches its UI to collect a screenshot. Notably, App Protocols are fire-and-forgetmeaning that the handler has no direct way to return data back to the browser that invoked the protocol.

The power and simplicity of App Protocols comes at a cost. They are the easiest route out of browser sandboxes and are thus terrifying, especially because this exploit vector is stable and available in every browser from legacy IE to the very latest versions of Chrome/Firefox/Edge/Safari.

What’s the Security Risk?

A number of issues make App Protocols especially risky from a security point-of-view.

Careless App Implementation

The primary security problem is that most App Protocols were designed to address a particular scenario (e.g. a “Meet Now” page on a videoconferencing vendor’s website should launch the videoconferencing client) and they were not designed with the expectation that the app could be exposed to potentially dangerous data from the web at large.

We’ve seen apps where the app will silently reconfigure itself (e.g. sending your outbound mail to a different server) based on parameters in the URL it receives. We’ve seen apps where the app will immediately create or delete files without first confirming the irreversible operation with the user. We’ve seen apps that assumed they’d never get more than 255 characters in their URLs and had buffer-overflows leading to Remote Code Execution when that limit was exceeded. The list goes on and on.

Poor API Contract

In most cases3, App Protocols are implemented as a simple mapping between the protocol scheme (e.g. “alert”) and a shell command in the registry, e.g. 


When the protocol is invoked by the browser, it simply bundles up the URL and passes it on the command line to the target application. The app doesn’t get any information about the caller (e.g. “What browser or app invoked this?“, “What origin invoked this?“, etc) and thus it must make any decisions solely on the basis of the received URL.

Until recently, there was an even bigger problem here, related to the encoding of the URL. Browsers, including Chrome, Edge, and IE, were willing to include bare spaces and quotation marks in the URL argument, meaning that an app could launch with a command line like:

alert.exe "alert:This is an Evil URL" --DoSomethingDangerous --Ignore=This"

The app’s code saw the –DoSomethingDangerous “argument”, failed to recognize it as a part of the URL, and invoked dangerous functionality. This attack led to remote code execution bugs in App Protocol handlers many times over the years. 

Chrome began %-escaping spaces and quotation marks8 back in Chrome 64, and Edge 18 followed suit in Windows 10 RS5.

Chromium limits URLs to 2048 characters, but still shows the confirmation prompt for longer URLs.

You can see how your browser behaves using the links on this test page.

Future Opportunity: A richer API contract that allows an App Protocol handler to determine how specifically it was invoked would allow it to better protect itself from unexpected callers. Moving the App Protocol URL data from the command line to somewhere else (e.g. stdin) might help reduce the possibility of parsing errors.


The application that handles the protocol typically runs outside of the browser’s sandbox. This means that a security vulnerability in the app can be exploited to steal or corrupt any data the user can access, install malware, etc. If the browser is running Elevated (at Administrator), any App Protocol handlers it invokes are launched Elevated; this is part of UAC’s design.

Because most apps are written in native code, the result is that most protocol handlers end up in the DOOM! portion of this diagram:



In most cases, the only4 thing standing between the user and disaster is a permission prompt.

In Internet Explorer, the prompt looked like this:

As you can see, the dialog provides a bunch of context about what’s happening, including the raw URL, the name of the handler, and a remark that allowing the launch may harm the computer.

Such information is lacking in more modern browsers, including Firefox:


…and Edge/Chrome:


Browser UI designers (reasonably) argue that the vast majority of users are poorly equipped to make trust decisions, even with the information in the IE prompt, and so modern UI has been greatly simplified5

Unfortunately, lost to evolution is the crucial indication to the user that they’re even making a trust decision at all [Issue].

Eliminating Prompts

Making matters more dangerous, everyone hates security prompts that interrupt non-malicious scenarios. A common user request can be summarized as “Prompt me only when something bad is going to happen. In fact, in those cases, don’t even prompt me, just protect me.

Unfortunately, the browser cannot know whether a given App Protocol invocation is good or evil, so it delegates control of prompting in two ways:

In Internet Explorer and Edge (version<=18), the browser respects a per-protocol WarnOnOpen boolean in the registry, such that the App itself may tell the browser: “No worries, let anyone launch me, no prompt needed.

In Firefox, Chrome, and Edge (version >= 76), the browser instead allows the user to suppress further prompts with an “Always open links of this type in the associated app” checkbox.

If the user selects this option, the protocol will silently launch in the future without the browser first asking permission.

However, Edge/Chrome version 77.0.3864 removed the “Always open these types of links in the associated app” checkbox.

The stated reason for the removal is found in Chrome issue #982341:

No obvious way to undo “Always open these types of links” decision for External Protocols.

We realized in a conversation around issue 951540 that we don’t have settings UI
that allows users to reconsider decisions they’ve made around external protocol
support. Until we work that out, and make longer-term decisions about the
permission model around the feature generally, we should stop making the problem
worse by removing that checkbox from the UI.

A user who had ticked the “Always open” box has no way to later review that decision6, and no obvious way to reverse it. Almost no one figured out that using the “Clear Browsing Data > Cookies and other site data” dialog box option directs Chrome to delete all previous “Always open” decisions for the user’s profile. 

Particularly confusing is that the “Always open” decision wasn’t made on a per-site basis– it applies to every site visited by the user in that browser profile.

Update 1 of 2An Enterprise policy for v79+ allows administrators to restore the checkbox. End users can import this registry script.

Future Opportunity: Much of the risk inherent in open-without-prompting behavior comes from the site that any random site ( can abuse ambient permission to launch the protocol handler. If browsers changed the option to “Always allow this site to open this protocol”, the risk would be significantly reduced, and a user could reasonably safely allow, e.g. to open the msteams protocol without a prompt.

Update 2 of 2: Microsoft Edge 82 introduced this change.

Alternatively, perhaps the Registry-based provisioning of a protocol handler should explicitly list the sites allowed to launch the protocol, akin to the SiteLock protection for legacy ActiveX controls.

For some schemes7 , Chrome will not even show a prompt because the protocol is included on a built-in allow or deny list.

Some security folks have argued that browsers should not provide any mechanism for skipping the permission prompt. Unfortunately, there’s evidence to suggest that such a firm stance might result in vendors avoiding the prompt by choosing even riskier architectures for Web-to-App communication. More on this in a future post.

Zero-Day Defense

Even when a zero day vulnerability in an App Protocol handler is getting exploited in the wild (e.g. this one), browsers have few defenses available to protect users. 

Unlike, say, file downloads, where the browser has multiple mechanisms to protect users against newly-discovered threats (e.g. file type policies and SmartScreen/SafeBrowsing), browsers do not presently have rapid update mechanisms that can block access to known vulnerable App Protocol handlers.

Future Opportunity: Use SafeBrowsing/SmartScreen or a file-type-policies style Component Update to supply the client with a list of known-vulnerable protocol handlers. If a page attempts to invoke such a protocol, either block it entirely or strongly caution the user.

To improve the experience even further, the blocklist could contain version information such that blocking/additional warnings would only be shown if the version of the handler app is earlier than the version number of the app containing the fix. 

Antivirus programs typically do monitor all calls to ShellExecute and could conceivably protect against malicious invocation of app protocol handlers, but I am not aware of any having ever done so.

IT Administrators can block users from launching protocols by listing them as rules in URLBlocklist policy:

REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\URLBlockList" /v "1" /t REG_SZ /d "exampleBlocked:*" /f

Privacy Concerns Prevent Protocol Detection

One of the most common challenges for developers who want to use App Protocols for Web-to-App communication is that the web platform does not expose the list of available protocol handlers to JavaScript. This is primarily a privacy consideration: exposing the list of protocol handlers to the web would expose a significant amount of fingerprintable entropy and might even reveal things about the user’s interests and beliefs (e.g. a ConservativeNews App or a LGBTQ App might expose a protocol handler for app-to-app communication).

Internet Explorer and Edge <= 18 supply a non-standard JavaScript function msLaunchUri that allows a web page to detect that a user didn’t have a to-be-invoked protocol handler installed, but this function is not available in other browsers.

UX When a Protocol Isn’t Installed

Browser behavior varies if the user attempts to invoke a link with a scheme for which no protocol handler is registered.

Firefox shows an error page: FirefoxNotInstalled

On Windows 8 and later, IE and Edge<=18 show a prompt that offers to take the user to the Microsoft Store to search for a protocol handler for the target scheme:


Unfortunately, this search is rarely fruitful because most apps are not available in the Microsoft Store.

Interestingly, Chrome and Edge76+ show nothing at all when attempting to invoke a link for which no protocol handler is installed. Surprisingly, there’s no notice even in the Developer Tools console; a particularly thorough debugger will only see a “(canceled)” request in the DevTool’s Network tab.

Upcoming change – Require HTTPS to Invoke

Chrome is looking at requiring that a page be served over HTTPS in order for it to invoke an application protocol.


In future posts, I’ll explore some other alternatives for Web-to-App communication.



1 In some browsers, it’s possible to register web-based handlers for “AppProtocols” (e.g. maps: and mailto: might go to Google Maps and GMail respectively). This mechanism is relatively little-used.

2 Within Chromium, App Protocols are called “External Protocols.”

3 There are other ways to handle protocols, including COM and the Windows 10 App Model’s URI Activation mechanism but these are uncommon.

4 As an anti-abuse mechanism, the browser may require a user-gesture (e.g. a mouse click) before attempting to launch an App Protocol, and may throttle invocations to avoid spamming the user with an infinite stream of prompts.

5 Chrome’s prompt used to look much like IE’s.

6 Short of opening the Preferences for the profile in Notepad or another text editor. E.g. after choosing “Always open” for Microsoft Teams and Skype for Business, the JSON file %localappdata%\Microsoft\Edge SxS\user Data\default\preferences contains

“protocol_handler”:{“excluded_schemes”:{“msteams”:false, “ms-sfb”: false}}

To see the list in IE/Edge<=18, you can run a registry query to find protocols with WarnOnOpen set to 0:

reg query "HKCU\SOFTWARE\Microsoft\Internet Explorer\ProtocolExecute" /s
reg query "HKLM\SOFTWARE\Microsoft\Internet Explorer\ProtocolExecute" /s

7 Hardcoded schemes:

kDeniedSchemes[] = {“afp”,”data”,”disk”,”disks”,”file”,”hcp”,”ie.http”,”javascript”,”ms-help”,”nntp”,”res”,”shell”,”vbscript”,”view-source”,””}
kAllowedSchemes[] = {“mailto”, “news”, “snews”};

The EscapeExternalHandlerValue function:

// Escapes characters in text suitable for use as an external protocol handler command. // We %XX everything except alphanumerics and -_.!~*'() and the restricted // characters (;/?:@&=+$,#[]) and a valid percent escape sequence (%XX). EscapeExternalHandlerValue()