As web developers, we should care about the privacy of our users. This article explains what the Referer header is and what information it exposes. We also learn to use the Referrer-Policy to control how many details the referer header should include. We can increase privacy and deal with some potential security issues by doing all of the above.
The Referer header
When we visit a website, it can contain a clickable link that redirects us to a different page. When we click on it, the browser might send a Referer request header to the server of the page we request. It contains the website’s address that includes the link we’ve used.
The word referer is a misspeling of the word referrer.
To observe this, let’s visit the wanago_io page on Twitter. It contains a bunch of links that lead to wanago.io.
When we click on the above, we get redirected to https://t.co/QtA0ttrRn3. The https//t.co address is a domain Twitter uses for shortening links. Let’s investigate its response:
1 2 3 4 5 6 7 8 9 10 |
<head> <noscript> <META http-equiv="refresh" content="0;URL=https://dev.wanago.io/2022/03/21/iframe-clickjacking-x-frame-options-content-security-policy/"> </noscript> <title>https://dev.wanago.io/2022/03/21/iframe-clickjacking-x-frame-options-content-security-policy/</title> </head> <body> <a href="https://dev.wanago.io/2022/03/21/iframe-clickjacking-x-frame-options-content-security-policy/"></a> <script>window.opener = null; document.getElementsByTagName("a")[0].click();</script> </body> |
Above, we can see that when we open https://t.co/QtA0ttrRn3, it immediately redirects us to wanago.io.
Thanks to the browser sending the referer: https://t.co/ header when requesting wanago.io, I know that the person that visited the blog came from Twitter.
Security concerns around referer
Unfortunately, the Referer header carries some privacy and security risks. It is fine as long as the Referer information is used for logging or analytics. However, the Referer mechanism can be abused to track or steal sensitive information.
Imagine having the functionality of resetting a password. When the users request a new password, they get an email with a link such as this one:
https://your-website/reset-password?token=secret_token
When the users visit the above URL, they might click on a link before finalizing setting the new password. When that happens, some other site might get the full URL along with the secret token that we use to authorize changing the password.
Dealing with the security issues using Referrer-Policy
Thankfully, there are ways to control what information the browser includes in the referer header. One of them is using the Referrer-Policy header.
Even though the Referer header contains the misspelled word referrer, the Referrer-Policy is spelled correctly.
There are quite a few values to choose from when setting up the Referrer-Policy.
strict-origin-when-cross-origin
The strict-origin-when-cross-origin value is a default referrer policy. It means that a browser is free to send the origin, path, and the query string in the Referer header when making a same-origin request:
- the user visits https://dev.wanago.io/courses/api-with-nestjs/
- clicks on a link leading to https://dev.wanago.io/2020/05/11/nestjs-api-controllers-routing-module/
- the request contains the referer: https://dev.wanago.io/courses/api-with-nestjs/.
An important thing to realize is that the browser omits some parts of the URL when sending the Referer header, even in the above case. A good example is the fragment, which is the part of the URL after the # sign. The above is yet another consequence of using hash-based routing. Check out Comparing the HashRouter and the BrowserRouter in React applications for more.
If you want to know more about the parts of the URL that the browser leaves out when sending the Referer header, check out the documentation.
For the cross-origin requests, the browser sends only the page’s origin. The above means that the request would contain only the referer: https://dev.wanago.io/ value.
An important caveat is that the above would only happen if the security protocol stays the same. For example, if the user navigates from an HTTPS page to an HTTP website, the browser does not send the Referer header.
We should avoid including sensitive data in the URL. However, if we have to do that, the strict-origin-when-cross-origin setting ensures that the user won’t leak it outside of our website through the Referer header.
no-referrer
If we set up the Referrer-Policy to no-referrer, the browser does not send the Referer header.
The helmet library sets the no-referer value for the Referrer-Policy by default. If you want to know more about helmet, check out Increasing security of Express applications with the Helmet middleware.
unsafe-url
When we set the Referrer-Policy to unsafe-url, the browser sends the origin, path, and the query string regardless of the origin or the security protocol. Unfortunately, using this approach might cause a leak of private information, such as tokens.
no-referrer-when-downgrade
With the no-referrer-when-downgrade value, we send the origin, path, and the query string in the Referer header if the security protocol stays the same or improves. For example, if an HTTPS website redirects the user to an HTTP page, the browser doesn’t send the Referer header.
origin
If we set the Referrer-Policy to origin, the browser sends only the origin in the Referer header.
same-origin
When using the same-origin option, the browser sends the origin, path, and query string only for the same-origin requests.
strict-origin
With the strict-origin setting, the browser sends the origin through the Referer header when the security level stays the same.
origin-when-cross-origin
By setting the origin-when-cross-origin value, the browser sends the origin, path, and the query string through the Referer header for same-origin requests. On the other hand, the browser sends only the origin for cross-origin requests and when downgrading the security protocol.
Which option to choose?
After reading all of the above, we can conclude that the default strict-origin-when-cross-origin setting deals with most of the security concerns. However, if we want to increase the privacy of our users, we can consider setting the Referrer-Policy to no-referrer.
Setting the Referrer-Policy
If we serve our content using Node.js and Express, setting the Referrer-Policy is very straightforward.
1 2 3 4 |
app.use((request, response, next) => { response.set('Referrer-Policy', 'no-referrer'); next(); }) |
Besides doing it manually, like in the above snippet, we can also use the helmet library. It can set the Referrer-Policy header for us, besides other headers. If you want to know more, check out the Increasing security of Express applications with the Helmet middleware.
We can also alter the Referrer-Policy through HTML. One way to do that is using the <meta> element.
1 2 3 4 |
<head> <meta name="referrer" content="no-referrer"> <!–– ... ––> </head> |
Setting the policy directly on elements
Besides using <meta>, we can also use the referrerpolicy attribute with <a>, <script>, <img>, <area>, and <iframe> tags.
1 |
<a href="https://dev.wanago.io" referrerpolicy="no-referrer"> |
As an alternative, we can also use link="noreferrer" on <a>, <link>, and <area> elements.
1 |
<a href="https://dev.wanago.io" rel="noreferrer"> |
Summary
In this article, we’ve gone through the idea behind the Referer header. We’ve also learned what issues it might cause and how to deal with them. To do that, we’ve used the Referrer-Policy header and learned how to set it with Node.js and Express. The default referrer policy value would prevent the most significant issues, such as leaking sensitive data. Even though that’s the case, we can still increase our users’ privacy by turning off the Referer header. We also need to watch out and not set the Referrer-Policy to a value that might hurt our users.