Every time a browser navigates from your site to another domain, it can hand that destination a piece of the URL you came from – and getting the Referrer Policy header wrong is one of the quieter ways sites leak session tokens, internal paths, and search queries to advertisers, analytics vendors, and sometimes competitors. This matters more than most teams assume, because the default browser behavior isn’t as safe as it looks, and a lot of production sites are still shipping configurations that expose more than they intend.
What the Referrer header actually sends
When a user clicks a link or a page loads a third-party resource – an image, a script, an iframe – the browser can attach a Referer header (yes, misspelled in the HTTP spec since 1996) to the outgoing request. That header contains the URL of the page the user was just on. Depending on the policy in effect, this can be the full URL including query string and path, just the origin, or nothing at all.
The problem shows up fast in real applications. A password reset page at https://app.example.com/reset?token=8f3a2c91 that links out to a CDN, a font provider, or a support widget can leak that token straight into the target server’s access logs, and from there into any analytics pipeline that ingests referrer data. Internal search tools are another common offender – a query like /search?q=layoff+plans+q3+2026 sent as a referrer to a third-party ad script tells that vendor exactly what your users are searching for internally.
The default behavior browsers actually use
Chrome, Firefox, and Safari have all shipped strict-origin-when-cross-origin as their default since roughly 2020 (Chrome 85, Firefox 87). This sends the full URL on same-origin requests, only the origin (scheme + host, no path) on cross-origin HTTPS-to-HTTPS requests, and nothing at all when downgrading from HTTPS to HTTP.
That default is a reasonable baseline, but it’s not automatically what your site is using. If the server sends no Referrer-Policy header at all, and an older library or misconfigured CDN forces a legacy behavior, or the browser is an outdated build still running no-referrer-when-downgrade, the full path and query string can travel cross-origin. Relying on “the browser will handle it” is the myth worth retiring here – it handles it reasonably in 2026’s evergreen browsers, but plenty of embedded webviews, older Android WebView builds, and enterprise-locked browser deployments still don’t follow current defaults.
Setting the header correctly
The fix is a single response header, but the value matters:
Referrer-Policy: strict-origin-when-cross-origin
This is the sensible default for most sites – full referrer on same-origin navigation (useful for internal analytics), origin-only across domains, nothing on HTTPS-to-HTTP downgrades. It can be set server-side via Nginx (add_header Referrer-Policy “strict-origin-when-cross-origin”;), Apache, or through a meta tag as a fallback: <meta name=”referrer” content=”strict-origin-when-cross-origin”>.
For pages that genuinely can’t afford any leakage – payment flows, password reset links, account recovery pages, anything with tokens in the URL – no-referrer is the stronger choice. It strips the header entirely on every outgoing request, same-origin or not. The tradeoff is that internal analytics lose visibility into the referring page for those flows, which is usually an acceptable trade given what’s at stake.
Per-link control is available too, using the referrerpolicy attribute on individual anchor or img tags, which overrides the page-wide policy for that specific element. This is worth using on any outbound link embedded in a page that otherwise needs a looser policy for internal navigation tracking.
Where tokens end up leaking in practice
A pattern seen repeatedly in production audits: an e-commerce checkout page includes a live chat widget (Intercom, Zendesk, Drift) that loads its own iframe. If the checkout URL carries a session identifier or cart token in the query string and the page has no explicit Referrer-Policy, that token can land in the chat vendor’s server logs the moment the widget’s iframe initializes and fires its own outbound requests.
Password reset and magic-link login flows are the most consistently mishandled case, because the token in the URL is single-use and short-lived, which teams assume makes the referrer leak low-risk. It doesn’t – if an attacker captures the token in the brief window before it’s consumed, from a shared analytics dashboard, a compromised third-party script, or a misconfigured logging pipeline, it’s directly usable for account takeover.
Internal admin panels are the case teams forget entirely. A URL like /admin/users?filter=role:finance loaded on a page with an embedded YouTube video or Google Maps widget hands that path to Google’s servers on every page view, which is rarely a legal problem but is an unnecessary disclosure of internal application structure.
Common mistakes to avoid
Setting the policy only on the homepage and assuming it applies site-wide is one of the most frequent errors – Referrer-Policy is not inherited across all response types the way some security headers are, and single-page applications in particular need to verify the header is present on every server-rendered route, not just the initial document load.
Another common mistake is trusting the meta tag alone in a mixed environment. The meta tag works, but an explicit HTTP response header takes precedence and is more reliable across resource types like stylesheets and prefetch requests, so relying solely on the meta tag leaves gaps for non-document requests.
The third recurring issue is setting unsafe-url thinking it just means “permissive” without registering what it actually does – it sends the full URL, including query strings, on every request regardless of destination or protocol. It shows up occasionally as leftover debugging configuration that never got reverted, and it’s the most dangerous of the eight legal Referrer-Policy values.
How this fits into a broader header review
Referrer-Policy rarely gets flagged on its own; it usually turns up alongside missing or weak Content-Security-Policy and cookie flag issues during a wider security headers review. A practitioner auditing a site’s header posture checks Referrer-Policy in the same pass as CSP and cookie attributes, since they’re set in the same server config block and a gap in one often means a gap in the others. Running a full security checklist against the site periodically catches this kind of drift before it becomes a pattern across every new route added.
FAQ
Does Referrer-Policy affect SEO or analytics tracking?
It can reduce the referrer data your own analytics tool sees from internal same-origin navigation only if you choose a stricter value like no-referrer-when-downgrade combined with HTTP resources; strict-origin-when-cross-origin preserves full referrer data for same-origin traffic, so standard analytics setups are unaffected. Cross-origin analytics platforms will only see the origin, not the full path, which is the intended tradeoff.
Is Referrer-Policy the same as X-Frame-Options or CSP?
No. X-Frame-Options and the CSP frame-ancestors directive control whether your site can be embedded in an iframe elsewhere; Referrer-Policy controls what information leaves your site when a user navigates away or a resource loads from a third party. They address different data flows and both are usually needed together.
Can I set different Referrer-Policy values for different pages?
Yes, and it’s recommended for sensitive flows. Set a stricter value like no-referrer on the response for specific routes (password reset, checkout, admin) while using strict-origin-when-cross-origin as the site-wide default, since most web servers and frameworks support per-route header configuration.
Referrer-Policy is a one-line fix with an outsized effect on what a site quietly hands to every third-party script and link it touches, and it costs nothing to test – load a sensitive page with browser devtools open on the Network tab, click through to an external resource, and check what actually shows up in that outgoing request’s headers.
