Why You Should Not Use Protocol‑Relative URLs

Image by Natalya Letunova.

In Brief

Use an explicit https:// URL for an external resource, or an ordinary relative URL when the resource shares the page's origin. A protocolrelative // URL inherits the page's scheme, which means an HTTP page can still request it insecurely and the transport requirement is no longer explicit. The pattern solved a historical migration problem that modern HTTPS sites should not recreate.

When you link to another website or load an external resource, you will generally specify the protocol that the browser should use within the URL. For example, if you wanted to load React via the official CDN links, it would look something like this:

<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script><script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

The use of https:// at the beginning or src will force the visiting browser to load these resources with HTTPS. You could equally switch those out for simply http:// to load over HTTP instead (if the source allows).

On an HTTPS page, modern browsers block or upgrade many HTTP subresources rather than merely warning after execution. An HTTP page remains vulnerable to onpath modification, and a protocolrelative resource on that page inherits HTTP.

Traditionally, the answer to this has been to use protocolless (or Protocolrelative) URLs. These remove the protocol altogether (simply starting with a double slash: //), and allow the hosting page to determine which protocol is used. If the page itself is HTTP, then HTTP will be used, and vice versa.

Using the original React example:

<script src="//unpkg.com/react@17/umd/react.production.min.js"></script><script src="//unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

I touched upon it earlier, but just to be clear: don't do this for external resources. On an HTTPS page the URL inherits HTTPS, so the doubleslash form is not an extra risk there. The problem is that it also inherits HTTP on an insecure page and hides the intended scheme from the source.

Whilst obviously dropping those 'http' characters saves on a few bytes of data (although honestly are you really that strapped for space?), and removes a bit of decisionmaking from the development side, it also comes with some very severe downsides too.

For external resources, specify HTTPS explicitly rather than hiding the scheme. Sameorigin links do not need an absolute scheme. HTTPS encrypts data in transit, provides integrity, and helps authenticate the endpoint; it does not by itself secure the server, its files, or the wider business systems.

HTTP/2 is commonly deployed over TLS and can improve some workloads, but performance depends on the connection, server, content, and protocol negotiation. Security is the reason to require HTTPS, not a blanket speed guarantee.

A protocolrelative URL does not let the browser choose arbitrarily; it inherits the document's scheme. On an HTTP page that means an insecure request vulnerable to onpath interference, which should be unnecessary when building a website or app in 2022. The 2015 interference with GitHub is one example.

The bottom line is that external resource calls should specify HTTPS. For sameorigin page links, ordinary relative URLs such as /about/ are correct and inherit the secure origin.


Need a senior engineer involved?

I can work directly in the codebase, review the architecture, or support the team through delivery when the work needs more than extra hands.