by

Notes on implementing a Referrer Policy

: did more research, added more detail, changed my recommendation, and linked to the new test site.

What is the Referer header?

To understand referrer policies, first you must understand the Referer header.

The Referer header is sent by browsers as part of HTTP requests. They are a friendly indicator of which website we came from that we ended up requesting a given URL.

So for example:

  1. You are visiting http://example.com/foo/bar.
  2. You click on a link that takes you to http://www.pablobm.com/.
  3. When the browser sends a request for http://www.pablobm.com/, it includes the header Referer: http://example.com/foo/bar, indicating where we come from.

This is not the only scenario. This header is also sent when requesting assets (eg: images). For example:

  1. You are visiting http://example.com/foo/bar.
  2. The page includes the image http://example.com/image.jpg.
  3. When the browser sends a request for http://example.com/image.jpg, it includes the header Referer: http://example.com/foo/bar, indicating the page that references the image.

And similarly, this can be sent in Ajax requests indicating the URL that originated them.

Nitpick: note that Referer is misspelled (the middle r should be double). However Referrer-Policy is correctly spelled. This is because Referer came first (back in 1996), included the mistake, browsers adopted it, and now we can't simply change it as software that uses the original spelling would stop working. Going forward, I'll use the spelling "referrer", unless specifically referring (argh!) to the header verbatim.

What's this for, and can it be a problem?

Here are some uses for the Referer header:

However, this header may reveal information that you want secret. The classic scenario goes something like this:

  1. You use the "forgotten password" feature on website A.
  2. You receive the usual password reset email including a link to website A, which you follow. The URL will contain a secret string, unique to your request for a new password.
  3. The password reset page may include an image hosted on a third party site B (hotlinked).
  4. The URL, including the secret string, will be sent to site B in the Referer header.
  5. Someone listening in at site B could get this password reset URL, with the secret string, and reset the password before you do, effectively hijacking your account.

This scenario may seem far-fetched, but it's perfectly legitimate, and the sort of issue that you need to bear in mind when securing your websites. And it's not the only scenario: there may be other details that you don't want leaked, and could potentially be accessed by third parties through a similar mechanism. For example: just the fact that you visited a specific site could be sensitive, if it is a site banned by a repressive government.

This concern may not relevant to many. For those for whom it is, there is a way to control the referrers sent as a result of visiting your website: setting a Referrer Policy.

Quick summary and recommendation

If you have decided to define a referrer policy for your website, there are two aspects you should consider:

  1. Which specific policy to use.
  2. How to set this policy in place.

If you just want it in short, here's my current recommendation:

  1. Prefer <meta> tags over headers for wider compatibility.
  2. Use the same policy across whole domains, as opposed to specific policies for individual pages.
  3. Which policies are available depends on which browsers you want to support:
    • For modern browsers as well as old ones (Edge Legacy, IE11, old iOS and Android, etc): you may want to stick to never or origin.
    • If only supporting modern browsers: use one of the policies recommended by Mozilla: no-referrer, same-origin, strict-origin, or strict-origin-when-cross-origin.

If you want more detail, read on!

What policies are available?

There are a few options when it comes to referrer policies, and you can see the list of standard policies at MDN. They can look a bit complicated to wrap your head around but, in general, you can think of the options in two dimensions:

Here's a summary table:

policy \ referrer origin, path, query string origin nothing
unsafe-url always
origin always
no-referrer always
origin-when-cross-origin same origin other origin
same-origin same origin other origin
no-referrer-when-downgrade same or higher security lower security
strict-origin same or higher security lower security
strict-origin-when-cross-origin same origin other origin, same or higher security other origin, lower security

As I write these lines, Mozilla's InfoSec guidelines recommend using one of these values only: no-referrer, same-origin, strict-origin, or strict-origin-when-cross-origin.

Additionally, some old browsers do not support these policies. Instead they implement an old version of the spec that only defines four of them: default, always, origin, and never. Affected browsers include old iOS and Android devices, IE11 and "Legacy" Edge (more on Edge later).

I'll refer to these old policies as, well, the "old policies" from now on. Of these, default, always, and never are considered deprecated, while origin made it to the current spec.

How do I implement a policy?

There are two main ways to implement a referrer policy: an HTTP header, or an HTML <meta> tag.

Technically there are other options, such as setting specific policies for individual links or images. Or doing like Google does, altering all outgoing links to point to an intermediate page that then redirects to the intended, final URL. I'm not going to cover any of that here though, as that was outside the scope of my research.

Referrer policy as an HTTP header

The HTTP header is Referrer-Policy, followed by the name of the desired policy. For example:

1Referrer-Policy: strict-origin

Technically there could be several policies, comma-separated. This could be useful to provide a fallback policy for the old browsers, along with a "proper" one for modern browsers. However old browsers don't appear to recognise this header, defeating the purpose.

Also technically, you could send the same header twice, with different values. Again this can be a problem as your server-side framework may not support duplicate headers. For example, currently frameworks based on the Rack webserver interface don't support this. This includes the popular Ruby on Rails.

Referrer policy as a <meta> HTML tag

The HTML <meta> tag must have the attribute name="referrer" (correct spelling with 4 "r") and a content attribute with the name of the desired policy as value:

1<meta name="referrer" content="strict-origin" />

This appears to be supported by any browser aware of referrer policies, even if not all possible values will be understood. This is in opposition to using a header to specify the policy, which is only understood by modern browsers.

Declaring more than one policy

So there are standard policies, understood by modern browsers, and old policies, understood by older browsers. Is there a way to provide two policies, so that both modern and old browsers have something to work with? This would be consistent with the standard as drafted as I write these lines, which says:

11.1: (...) unknown policy values will be ignored, and when multiple sources specify a referrer policy, the value of the latest one will be used (...)

Unfortunately, this doesn't appear to work. If you provide two policies, old browsers will use the second policy, regardless of whether they understand it or not. Modern browsers also always apply the second policy, but they understand both old and new values, so this is consistent with the spec.

As for what browsers do with policies they don't understand, here's a table:

policy \ browser Old iOS/Safari1 Old Android2 Edge Legacy IE11
unsafe-url unsafe-url no-referrer no-referrer-when-downgrade unsafe-url
no-referrer no-referrer no-referrer
origin-when-cross-origin no-referrer-when-downgrade
same-origin
no-referrer-when-downgrade no-referrer-when-downgrade
strict-origin no-referrer
strict-origin-when-cross-origin

So for example, if you provide two policies: origin and strict-origin, then IE11 will apply no-referrer-when-downgrade, while an old Android browser will apply no-referrer. This is the same as if you had only provided strict-origin, as the first policy is always ignored.

Checking that this is working

So you have implemented a referrer policy after following the advice above. How do you know that it is correctly set up?

A simple method is using the network inspector in your browser. You can use it to verify which referrers are sent, as well as what policy applies to a given document.

For example, these are captures of the inspectors from Firefox 68 and Chrome 80 (actually Chromium on my Linux machine):

Examining the referrer policy on Firefox Examining the referrer policy on Chrome

Examining the referrer policy on Mozilla Firefox and Google Chrome

You can see:

  1. Which referrer policy was given by the server (in this case with a header).
  2. Which Referer value was sent by the browser, based on the previous policy.
  3. Which policy applied to a specific request, again based on the previous policy.
  4. As a bonus, on Firefox you can filter headers by name, helping you find them.

Remember that no referrer is sent in certain situations where it doesn't make sense. For example, if you enter a URL manually on your address bar, there's no referrer as you didn't follow a link.

Other considerations

The referrer policy can't be changed dynamically

You can't change the referrer policy dynamically using JavaScript on the browser. The policy set in the initial page request, be it in the headers or in the <meta> tag of your original document, will be the one that will stick for as long as the page lives. For example, it's not possible to change it by altering the DOM and changing the <meta> tag. This is on purpose, as allowing it to change would open the door to altering it maliciously via Cross-site Scripting.

The previous point is particularly important if your site works as a Single-Page Application, or if it uses a technology such as Turbolinks to speed up page load. In these cases, it won't be possible for different pages in your site to define different referrer policies. Only the one defined in the first page load will work for the rest of the session, until there's again a full-page reload.

For this reason, as well as to generally avoid accidents, I recommend using the same referrer policy across the whole domain.

Default policies set by frameworks

If you are seeing a referrer policy that you didn't expect, it might be that your framework sets a default value. For example, Ruby on Rails sends a header Referrer-Policy: strict-origin-when-cross-origin by default, I believe since version 5.2.0.

Edge vs. Edge Legacy

Until this research, I thought that MS Edge was an "evergreen" browser. In other words, that it updated automatically and always stayed at the latest version. This turned out to be not exactly true, at least at the moment.

There are two families of MS Edge out there. The original one, which I have seen dubbed "Edge Legacy", includes versions up to 44 and uses Microsoft's own EdgeHTML rendering engine.

Then there's "current" Edge. If a Windows user deliberately downloads and installs Edge, they will get this, which is indeed evergreen. It uses Google's Blink rendering engine, just as Google Chrome does.

As a result of this requirement to download and install, current Edge has only a fraction of the market share that Edge Legacy enjoys: 0.26% vs. 2.18% according to Statcounter Global Stats as of March 2020. Fortunately this is expected to change in the future, as Microsoft are planning on including it with Windows updates. Eventually.

Regarding referrer policies, Edge Legacy only supports old referrer policies. Current Edge supports the new policies.

And a last "fun" fact: Edge Legacy identifies itself as v18 in its User-Agent, even though the actual version can be as high as 44. From what I can tell, this refers to the version of the rendering engine EdgeHTML, as opposed to that of the browser.

Try yourself!

Writing this guide took me a lot of tedious, repetitive testing. My first version included some mistakes, and I'm sure this version has problems somewhere. So before taking all this from me at face value, how about you try out yourself?

To help with this, I created https://referrer-policy.info, a site providing many subdomains that implement different policies and combinations. Give it a whirl and tell me if you discover something I missed! You can point my mistakes out to me on Twitter at @pablobm.

  1. For old iOS, I tested an old iPad mini running 9.3.6

  2. For old Android, I tested the Android stock browser on an emulator running Android v22 (Lollipop)