Dev.to Security 🔐 Cybersecurity 👁 0 📖 7 min read

Referrer-Policy: Stop Leaking Sensitive URLs

Every time a user clicks from your app to a third-party link, the browser may send a Referer header with the full URL they came from—including path segments, query strings and sometimes one-time tokens. Password-reset li

Referrer-Policy: Stop Leaking Sensitive URLs

Every time a user clicks from your app to a third-party link, the browser may send a Referer header with the full URL they came from—including path segments, query strings and sometimes one-time tokens. Password-reset links, signed download URLs, invite codes and internal admin paths have all leaked this way into analytics vendors, CDNs and support widgets. Referrer-Policy is the response header that decides how much of that URL other origins are allowed to see. Set it wrong (or leave the browser default) and you quietly ship secrets to whoever sits at the other end of the click.

I build PixelValut, a set of browser-side developer and security tools, and I wanted a practical checklist for teams that already ship HTTPS but never audited what their pages put in Referer. I wrote this with help from AI tools and checked every command and claim myself. This piece covers the policy values that matter, real leak scenarios, how to set and verify the header with curl and DevTools, and a safe default for most SaaS apps.

What does Referrer-Policy actually control?

Direct answer: Referrer-Policy tells browsers how much of the current page URL to put in the Referer header (and in document.referrer) when the user navigates or loads a subresource. Policies range from sending nothing (no-referrer) to sending the full URL even on HTTPS→HTTP downgrades. Pick a value that keeps path and query off cross-origin requests unless you truly need them for analytics.

The policy values in one table

Policy Same-origin request Cross-origin HTTPS→HTTPS HTTPS→HTTP downgrade Typical use
no-referrer No Referer No Referer No Referer Highest privacy; breaks referrer-based analytics
no-referrer-when-downgrade Full URL Full URL No Referer Old browser default; still leaks path/query cross-origin
origin Origin only Origin only Origin only Soft analytics without paths
origin-when-cross-origin Full URL Origin only Origin only Good balance for many apps
same-origin Full URL No Referer No Referer Strict; third parties see nothing
strict-origin Origin only Origin only No Referer Origin for peers; strip on downgrade
strict-origin-when-cross-origin Full URL Origin only No Referer Current browser default on modern engines; solid SaaS default
unsafe-url Full URL Full URL Full URL Almost never justified

Modern Chrome, Firefox and Safari effectively default to strict-origin-when-cross-origin when the site sends no policy. That is better than the old no-referrer-when-downgrade default, but it still sends your full path and query to other pages on the same origin, and your origin (scheme + host + port) to other HTTPS sites. If a reset link lives at https://app.example.com/reset?token=… and the user clicks a cross-origin help article, the token stays off the wire—only https://app.example.com/ is sent. If that help widget is same-site or you used unsafe-url, the token can leave the browser.

Leak scenarios worth designing against

Treat these as design cases, not one-off bugs:

  1. Password-reset and magic-login URLs in emails. User opens the link, then clicks to an external status page or chatbot. Cross-origin navigations under a strict policy omit the query; a loose policy or a same-origin redirect chain can still expose it in logs.
  2. Signed download or S3-style URLs pasted into a webview that then loads third-party fonts, iframes or error beacons. Subresource requests also send Referer unless the policy strips them.
  3. Invite and share links with capability tokens in the query string. A recipient forwards the page; analytics on an outbound link inherits the referrer.
  4. Internal admin paths (/admin/users/123/impersonate). Even without tokens, path leakage tells an external script who you were looking at.
  5. Mixed content and HTTP images on an HTTPS page. Downgrade rules matter; unsafe-url and older defaults are how full URLs end up on cleartext hops.

This sits next to transport controls like MTA-STS for inbound email and certificate issuance limits via CAA records and CT logs: each closes a different disclosure channel. Referrer policy closes the browser-navigation channel.

Weak vs hardened header setups

Weak Hardened Why
No Referrer-Policy header Referrer-Policy: strict-origin-when-cross-origin Explicit beats relying on each browser's default
unsafe-url "so analytics works" origin or strict-origin-when-cross-origin plus server-side UTM on outbound links you control You do not need full paths in third-party logs
Token in query on a marketing page that loads many third-party scripts Move secrets to POST body, fragment, or one-time server session; keep same-origin or no-referrer on that route Headers cannot fix a secret that must never leave
Only a <meta name="referrer"> on some templates Send the HTTP header globally from the reverse proxy or app middleware Meta tags are easy to forget on error pages and APIs
Different policies per environment, undocumented One documented default in nginx/Cloudflare/Laravel, with per-route exceptions listed Drift is how unsafe-url survives in staging clones

A sealed envelope resting beside a browser window frame on a dark desk, representing URLs that should not leak through the Referer header
AI-generated image.

Set the header in nginx, Cloudflare and Laravel

Nginx (inside the server block):

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Cloudflare Transform Rule or Response Header modification:

Set Referrer-Policy = strict-origin-when-cross-origin

Laravel middleware (global):

public function handle($request, Closure $next)
{
    $response = $next($request);
    $response->headers->set(
        'Referrer-Policy',
        'strict-origin-when-cross-origin'
    );
    return $response;
}

You can also set a document-wide default with HTML, useful on static sites:

<meta name="referrer" content="strict-origin-when-cross-origin">

Prefer the HTTP header for APIs and redirects. For a single sensitive view (reset confirm, billing portal), tighten further:

Referrer-Policy: no-referrer

HTML links can override per navigation with the referrerpolicy attribute or rel="noreferrer":

<a href="https://partner.example/docs" referrerpolicy="no-referrer">Docs</a>
<a href="https://partner.example/docs" rel="noreferrer">Docs</a>

rel="noreferrer" also implies noopener behavior for browsing context, which pairs well with the reverse-proxy hardening patterns in zero-trust nginx setups.

Verify with curl, DevTools and PixelValut

Check that the response advertises the policy:

curl -sI https://pixelvalut.com/tools/ | grep -i referrer-policy

You want a line like:

referrer-policy: strict-origin-when-cross-origin

In Chrome DevTools:

  1. Open Network, click the document request, read Response Headers for referrer-policy.
  2. Click an outbound request to a third party and inspect Request Headers → referer.
  3. Confirm cross-origin calls show only the origin (or nothing), never ?token= or /reset/.

For a quick browser-side pass on any URL, use the PixelValut HTTP Status Checker to inspect response headers without installing anything. Then repeat the navigation test in DevTools—header presence alone does not prove outbound requests are stripped; you still need to watch a real cross-origin click.

What Referrer-Policy does not fix

Problem Why policy is not enough Better control
Secret in the URL bar copied by the user No browser header involved Prefer body/fragment/session; short TTL tokens
Server-side logs of your own full URLs Same-origin full referrers still apply under several policies Redact query strings in access logs
Third-party script reading document.referrer same-origin Policy allows full URL same-origin by design under the recommended default no-referrer / same-origin on sensitive pages; CSP script allowlists
Email clients prefetching links Different client stack Single-use tokens; do not put long-lived secrets in query strings

Rollout checklist

  • [ ] Choose a site-wide default: start with strict-origin-when-cross-origin
  • [ ] Send it as an HTTP response header from the edge or app (not only a meta tag)
  • [ ] Tighten reset, invite and billing routes to no-referrer or same-origin
  • [ ] Ban unsafe-url in code review and config lint
  • [ ] Move capability tokens out of query strings where you can
  • [ ] Verify with curl -sI and a DevTools cross-origin click
  • [ ] Spot-check staging and error pages—they often miss global middleware
  • [ ] Document outbound links that truly need origin-level analytics

Frequently asked questions

Is strict-origin-when-cross-origin safe enough for SaaS?

For most product apps, yes as a default. It keeps path and query off cross-origin requests and strips referrers on HTTPS→HTTP downgrades. Sensitive flows (password reset, impersonation, billing) still deserve no-referrer or same-origin.

Will this break Google Analytics or ads?

It can reduce the path-level detail third parties receive. Origin-only or missing referrers are common under modern defaults. If you need campaign attribution, put UTM parameters on links you control and accept that full-path referrers to arbitrary third parties are not worth the leak.

What is the difference between Referrer-Policy and rel="noreferrer"?

The header (or meta tag) sets a document default. rel="noreferrer" and the referrerpolicy attribute override per link. Use the header globally, then override on specific outbound anchors.

Does Referrer-Policy affect APIs and fetch calls?

Yes. fetch, XHR and subresources send Referer according to the policy (and the request's referrerPolicy option). A global header covers navigations and most subresources; explicit referrerPolicy: "no-referrer" in fetch helps for sensitive XHRs.

Should I use no-referrer everywhere?

You can, if you do not rely on inbound referrers for security or analytics. Many teams prefer strict-origin-when-cross-origin site-wide and no-referrer only on secret-bearing pages so first-party analytics still see full same-origin paths.

How do I test a leak before changing production?

Open a staging page whose URL contains a dummy ?token=test, click a cross-origin link or load a cross-origin image, and inspect the outbound Referer in DevTools. Repeat after setting the header until the token disappears from cross-origin requests.

Follow & Connect

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.