Also available in: Español · Português · Français · العربية
Referrer-Policy: every value compared, and the rule nobody mentions
Work out which policy is in force, what a given request would actually put in the Referer header, and why sending two values is a documented technique.
What the Referrer-Policy header does
When a browser follows a link or fetches a resource, it usually tells the destination where the request came from, in a header called Referer. Referrer-Policy is how a site controls how much of that it gives away — the full URL with its path and query, only the origin, or nothing at all. There are eight values, and the specification defines each one against three situations: a request to the same origin, a request to a different origin, and a request that leaves a secure page for an insecure destination.
The default, if you send no header at all, is strict-origin-when-cross-origin. That sends the full URL within your own site, only the origin when leaving it, and nothing when the destination drops to plain HTTP. It is a sensible default, and it is worth knowing, because a header that a browser cannot parse leaves you with exactly this and nothing else.
One small thing the specification points out about its own name: the header does not repeat the famous misspelling. The request header is Referer with one r, a typo old enough to be permanent. The response header that controls it is Referrer-Policy, spelled properly. Getting them the same way round is a common way to write a header that silently does nothing.
How to use it
- Paste your header. One header per line if you send more than one — a browser reads several Referrer-Policy headers as a single comma-separated list. The panel underneath shows each value and says which one a browser will actually use.
- Put in the two URLs. The page doing the linking and the destination. Both need a scheme, because the scheme is what decides whether the request counts as same-origin, cross-origin, or a downgrade, and those three are the only distinctions any of the policies make.
- Read the Referer row. It shows the exact header the request would carry, or says that no header is sent. The table at the bottom puts all eight policies side by side, with the one in force highlighted.
Why the last value wins, and why that is useful
This is the part that catches people out, and it is written down clearly. The specification says that unknown policy values will be ignored, and that when multiple sources specify a referrer policy, the value of the latest one will be used. Not the first. The parsing algorithm is three steps long: start with nothing, then for every token in the header, if it is a policy you recognise, set the policy to it. Whatever you recognised last is what you end up with.
That inverts the usual instinct. A header value that looks like a list normally means alternatives in order of preference, and the natural way to write a parser is to take the first thing you understand and stop. Here you must keep going, and a parser that stops early gets the wrong answer on the specification's own example.
The reason is deployment. The specification works the case itself: send origin followed by unsafe-url, and a browser too old to know unsafe-url ignores it and uses origin, while a current browser walks to the end and uses unsafe-url. So Referrer-Policy: origin, unsafe-url is not a mistake or a duplicated line — it is the documented way to adopt a new policy value without abandoning older clients. The grammar is built for it: the specification permits an unknown token specifically so that a browser does not fail to parse the entire header field when it meets one.
The same rule has a sharper edge. If nothing in your header is recognised — a typo, a value from a blog post that never shipped, a policy name with an underscore in it — the browser is left with the empty string and falls back to its default. The header did nothing at all, and nothing in the response tells you so. That is what the panel above reports first.
What each policy actually sends
The eight-by-three table on this page is transcribed from the sentence in the specification that defines each policy, rather than assembled from memory or from other people's tables. Two rows are worth reading twice, because they are the ones most often mixed up.
origin and strict-origin look nearly identical — both send only the origin — and they differ on exactly one square. origin sends it everywhere, including to plain HTTP; strict-origin withholds it on that downgrade. The specification is explicit about the reason, noting that origin allows the origin of HTTPS referrers to be sent over the network as part of unencrypted HTTP requests, and that strict-origin addresses this concern. The same pair repeats one level up: origin-when-cross-origin and strict-origin-when-cross-origin differ only in the downgrade column.
There is also a case that surprises people who think of origins as being about hostnames. An origin includes the scheme, so moving from http to https on the same host is a cross-origin request, not a same-origin one — the specification calls out protocol upgrades explicitly. A different port does the same thing. If a policy behaves differently than you expect for a request that stays on your own domain, this is usually why.
And unsafe-url means what it says. The specification adds a note that reads almost like an apology: the policy's name doesn't lie, it is unsafe, and it will leak origins and paths from secure resources to insecure origins. Its worked example uses a document called sekrit.html for a reason.
What this will not tell you
This works out what a policy means. It does not know what your server sends. A browser cannot read another site's response headers for you, so the header in the box is the one you typed, not the one in production — check that with your browser's network panel or a request from the command line.
The classification of a destination as secure or not is a simplification, and worth stating. The specification defers to a separate standard for what counts as a potentially trustworthy URL, and that algorithm covers more ground than this page implements: https, wss and file, plus localhost and the loopback addresses, are treated as trustworthy here and everything else is not. That covers every example in the specification and every case you are likely to type, but a scheme registered as trustworthy by a browser extension or a packaged application will be judged wrongly.
A page's policy is also not the only thing in play. A single link can override it with a referrerpolicy attribute, rel=noreferrer removes the referrer entirely, an iframe can set its own, and a redirect can change the policy partway through a request. This page answers the question for one policy and one pair of URLs, which is the question the header is about, and the rest are per-element decisions made in your markup.
Finally, none of this is a privacy guarantee. Suppressing the Referer header stops one specific leak; it does nothing about a URL you passed in a query string, a script that reports the location itself, or anything the destination can infer from the request in other ways. It is one useful control, not a curtain.
Why is it free?
Reading a header and comparing two URLs is text work, and it runs in your browser. There is no server in the loop, so there is nothing to bill for and no account to create.
Nothing is uploaded and nothing is stored. Reload the page and it has forgotten the URLs you typed.