The Referer header, explained

The browser uses the Referer header to tell a server which page a request came from. It's behind analytics, hotlink protection and a surprising number of "works on my machine" bugs. This guide covers what it contains today, why it's often shorter than you expect, and how to set it when you need to test.

On this page
  1. What it contains
  2. Why it's spelled "Referer"
  3. Why it's empty or only the domain
  4. Referrer-Policy values
  5. How to set or change it for testing
  6. If your server checks it

What it contains

When you click a link on https://blog.example.com/post/42 to another site, the browser's request looks like this:

GET /pricing HTTP/1.1
Host: shop.example.org
Referer: https://blog.example.com/

The same goes for images, scripts, stylesheets and fetch() calls a page makes: each can carry the page's address. Fragments (#section) and any username or password in the URL are never included.

Why it's spelled "Referer"

A typo in the 1996 HTTP specification. By the time anyone noticed, servers everywhere read Referer, so it stayed. The newer Referrer-Policy header, the JavaScript document.referrer property and the referrerpolicy attribute are all spelled correctly. Mixing them up is a classic bug: a server that reads Referrer gets nothing.

Why it's empty or only the domain

Since Chrome 85 (2020), and in Firefox and Safari too, the default policy is strict-origin-when-cross-origin. That means:

Request goes toReferer sent
The same origin (same scheme, host and port)Full URL: https://blog.example.com/post/42?ref=nav
Another origin on the same site (blog. → shop.example.com)Origin only: https://blog.example.com/
Another site, over httpsOrigin only: https://blog.example.com/
Any site over plain http, from an https pageNothing

It can also be missing because the page set a stricter policy, the link has rel="noreferrer", the user typed the address or used a bookmark, or a privacy extension stripped it.

Referrer-Policy values

A site controls what its pages send with a response header or a meta tag:

Referrer-Policy: strict-origin-when-cross-origin
<meta name="referrer" content="no-referrer">
ValueWhat gets sent
no-referrerNever anything
same-originFull URL to your own origin, nothing elsewhere
originOnly the origin, everywhere
strict-originOnly the origin, and nothing on https→http
strict-origin-when-cross-originFull URL on your own origin, origin elsewhere (even your other subdomains), nothing on https→http (the default)
no-referrer-when-downgradeFull URL everywhere except https→http (the old default)
unsafe-urlFull URL, everywhere, including query strings. Avoid it: tokens in URLs leak this way.

How to set or change it for testing

You can't set it from page JavaScript: Referer is a forbidden header, so fetch(url, { headers: { Referer: "..." } }) is silently ignored. The referrer option of fetch only accepts a URL on your own origin. Your options:

From the command line

curl -e "https://partner.example.com/" https://api.example.com/image.png
curl -H "Referer: https://partner.example.com/" https://api.example.com/image.png

In your browser, for the sites you choose

A header extension can set, replace or remove Referer on requests to one site, which is how you test hotlink protection, a partner-only embed, or an analytics filter in a real browser session.

HeaderForge (Chrome, free)

Set, replace or remove any request header, Referer included, for all sites or only for URL patterns you pick, and switch it off with one click. It tells you whether the rule actually fired. No account, no tracking.

Get HeaderForge for Chrome

If your server checks it

Referer is fine for analytics and for casual hotlink protection. It is not a security check: anyone can send any value from curl, and many real visitors send none at all because of the policies above. For access control, use a token. For protecting forms against cross-site requests, use SameSite cookies and a CSRF token, and check the Origin header, which browsers send on cross-site POSTs and which pages can't fake.

Related: add any custom header to requests in Chrome · change your user agent · X-Frame-Options and "refused to connect" · what CORS is.

More on this topic: Cache-Control header explained: max-age, no-cache, no-store and more · “Refused to load … violates the following Content Security Policy directive”: fixes · Mixed content error: “loaded over HTTPS, but requested an insecure resource” · ModHeader alternative