Antoine Duno
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
- The Referer header silently leaks URL fragments, session tokens, and user paths to third parties on every navigation. The Referrer-Policy header gives you precise control over what gets shared — and most sites are using the wrong setting.
- Includes copy-paste code examples and step-by-step instructions.
- Free automated scan available to verify your implementation.
Referrer-Policy Explained: Control Data Leakage from Your Website
When a user clicks a link on your website and navigates to another site, or when your page loads external resources (images, scripts, fonts, analytics), the browser sends a Referer header to those external servers. That header contains the URL of the page the request originated from.
Most developers treat this as a minor technicality. It is not. In 2026, the combination of complex URLs, third-party analytics, CDNs, and embedded content means that the Referer header routinely transmits data you did not intend to share — sometimes including session tokens, user-specific identifiers, and sensitive path parameters.
The Referrer-Policy header (note: "Referrer" is spelled correctly in the header name, unlike the historical typo in the Referer request header) is your control mechanism.
What the Referer Header Actually Leaks
Before examining the policy values, it helps to understand concretely what the Referer header exposes.
Token leakage in URLs:
https://app.example.com/reset-password?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...If this page includes a tracking pixel, Google Analytics snippet, or any third-party resource, the full URL — including the password reset token — is sent to that third party in the Referer header.
User ID exposure:
https://app.example.com/users/84729/settingsLoading a Google Font or any external resource from this page sends the user ID to Google or the CDN provider. Depending on your terms of service and privacy policy, this may constitute an undisclosed transfer of personal data.
Search query leakage:
https://app.example.com/search?q=John+Smith+accountant+LondonIf your search results page loads external resources, the search query is transmitted to those providers.
API endpoint exposure:
https://internal-admin.example.com/api/users/bulk-export?format=csv&scope=allInternal tool URLs can reveal internal API structure, admin functionality, and operational data.
The third-party scope problem: When you add a Google Analytics script, a Hotjar heatmap, a Stripe payment form, a social sharing button, or an embedded video to your page, each of these loads resources from external servers. Each of those resource requests carries the full Referer URL from the page that loaded them — unless you have set a Referrer-Policy.
All 8 Referrer-Policy Values Explained
The specification defines eight directive values. Understanding each one is necessary to choose correctly.
### 1. no-referrer
No Referer header is sent on any request, regardless of origin or protocol.
Use when: Maximum privacy is required — internal tools, admin panels, health or financial applications. Trade-off: Analytics and some legitimate third-party integrations may break or lose useful context.
### 2. no-referrer-when-downgrade
Sends the full URL as Referer when navigating to HTTPS, but sends nothing when navigating from HTTPS to HTTP (downgrade).
This was the browser default before 2020. It is not a secure default for most applications — it sends full URLs including tokens and paths to all HTTPS destinations.
Use when: You need legacy compatibility and are certain your URLs contain no sensitive data.
### 3. origin
Sends only the origin (scheme + host + port) — never the path or query string.
# Full URL: https://app.example.com/users/84729/settings
# Referer sent: https://app.example.comUse when: You want analytics providers to know traffic came from your domain but not which specific pages. Some third-party integrations need the origin for attribution.
### 4. origin-when-cross-origin
Sends the full URL for same-origin requests (within your own domain) and only the origin for cross-origin requests.
# Same-origin request (within app.example.com):
# Referer sent: https://app.example.com/users/84729/settings
# Cross-origin request (to analytics.thirdparty.com):
# Referer sent: https://app.example.comUse when: You want internal analytics to see full page paths but external services to see only your domain.
### 5. same-origin
Sends the full URL for same-origin requests. Sends nothing for cross-origin requests.
Use when: You only care about same-origin navigation tracking (internal analytics), and you want complete privacy from all external parties.
### 6. strict-origin
Sends only the origin for same-protocol requests (HTTPS to HTTPS, HTTP to HTTP). Sends nothing for downgrade (HTTPS to HTTP) requests.
This is similar to origin but adds protection against protocol downgrade.
### 7. strict-origin-when-cross-origin
- Same-origin requests: full URL is sent
- Cross-origin same-protocol requests (HTTPS to HTTPS): only origin is sent
- Protocol downgrade (HTTPS to HTTP): nothing is sent
This is the recommended default for most applications. It provides useful same-origin navigation tracking for your own analytics while protecting path and query data from external parties, and preventing any leakage to non-HTTPS destinations.
### 8. unsafe-url
Always sends the full URL regardless of origin or protocol. No privacy protection whatsoever.
Use when: Never in production. This is the least restrictive option and exists primarily for testing.
Policy Behavior Summary
| Policy | Same-Origin | Cross-Origin HTTPS | Cross-Origin HTTP |
|---|---|---|---|
no-referrer | — | — | — |
no-referrer-when-downgrade | Full URL | Full URL | — |
origin | Origin | Origin | Origin |
origin-when-cross-origin | Full URL | Origin | Origin |
same-origin | Full URL | — | — |
strict-origin | Origin | Origin | — |
strict-origin-when-cross-origin | Full URL | Origin | — |
unsafe-url | Full URL | Full URL | Full URL |
Which Policy to Choose
For most web applications in 2026:
`strict-origin-when-cross-origin` — the right default. Your own analytics (if same-origin) still see full page paths. Third-party services only know your domain. No leakage over non-HTTPS channels.
`no-referrer` — for applications handling especially sensitive data: healthcare portals, legal platforms, financial tools, internal admin panels. The slight loss of analytics fidelity is worth the privacy guarantee.
`origin-when-cross-origin` — if you rely on cross-origin analytics that need some URL context but not full paths. Less recommended in 2026 given the privacy implications.
Avoid no-referrer-when-downgrade and unsafe-url in any user-facing production application.
GDPR Implications
Referrer leakage has a direct relationship with GDPR''s data minimization and lawful transfer requirements. When the full URL of a page (which may contain personal data — user IDs, search terms, token fragments) is sent to a third-party analytics or CDN provider:
- 1That third party becomes a data recipient under GDPR
- 2If they are outside the EU/EEA (e.g., US-based analytics), a lawful transfer mechanism is required
- 3The transfer must be documented in your Article 30 Records of Processing Activities
- 4Users must be informed in your privacy policy
Setting Referrer-Policy: strict-origin-when-cross-origin or stricter is a direct technical implementation of GDPR''s data minimization principle — you share only what is necessary (the domain, for basic attribution) rather than everything (the full URL with personal data).
Several EU DPA enforcement actions have cited unnecessary data sharing with analytics providers via referrer headers as evidence of inadequate privacy practices.
How to Implement Referrer-Policy
Nginx
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Apache
Header always set Referrer-Policy "strict-origin-when-cross-origin"Express.js (with Helmet)
import helmet from ''helmet'';
app.use(helmet({
referrerPolicy: {
policy: ''strict-origin-when-cross-origin''
}
}));Next.js (next.config.js)
module.exports = {
async headers() {
return [
{
source: ''/(.*)'',
headers: [
{
key: ''Referrer-Policy'',
value: ''strict-origin-when-cross-origin'',
},
],
},
];
},
};HTML Meta Tag (per-page)
<meta name="referrer" content="strict-origin-when-cross-origin">Use the meta tag when you cannot control server headers — for example, static HTML files or third-party CMS platforms.
Per-Link Control
You can override the global policy for individual links using the referrerpolicy attribute:
<!-- Override to send nothing for a specific sensitive external link -->
<a href="https://external-partner.com/signup" referrerpolicy="no-referrer">
Register with Partner
</a>
<!-- Override to send full URL for a specific same-origin link (for tracking) -->
<a href="/dashboard/overview" referrerpolicy="unsafe-url">
Dashboard
</a>This granularity is useful when you have a general policy of strict-origin-when-cross-origin but specific flows that require different behavior.
Browser Support
Referrer-Policy is supported in all modern browsers as of 2026:
- Chrome 56+ (2017)
- Firefox 52+ (2017)
- Safari 11.1+ (2018)
- Edge 79+ (2020)
Browsers that do not support the header fall back to their default behavior, which in modern browsers is strict-origin-when-cross-origin (Chrome 85+, Firefox 87+, Safari 14+). Older browsers may fall back to no-referrer-when-downgrade, which is the reason for setting the header explicitly rather than relying on defaults.
Verification
# Check your current header
curl -I https://yourapp.com | grep -i referrer-policy
# Expected output:
# referrer-policy: strict-origin-when-cross-originZeriFlow checks for Referrer-Policy and 6 other critical security headers as part of its 80+ check scan, with specific recommendations for which directive to use based on your application type. Run a free scan at zeriflow.com.