Back to blog

Privacy by Default at the HTTP Layer: Headers That Shrink Your Tracking Surface

Two response headers — Permissions-Policy and Referrer-Policy — decide how much your pages can leak to ad-tech and third parties. Set them once and the surveillance surface closes by default.

Most privacy work happens in JavaScript — consent logic, tag gating, identifier rotation. But the browser hands you two HTTP response headers that close the surveillance surface before a single script runs, and most sites ship neither. Permissions-Policy and Referrer-Policy are privacy-by-design in its most literal form: configuration, not code.

They matter more now than a year ago. With Google's October 2025 retirement of most Privacy Sandbox APIs, the residual ad-tech directives those APIs introduced are still wired into Chrome — and a site can now affirmatively switch them off instead of waiting for them to be removed.

Permissions-Policy: Deny the Features You Never Use

Permissions-Policy controls which browser features a document and its embedded frames may use. It is the same mechanism that gates camera, geolocation, and microphone — but it also gates the tracking-adjacent APIs that ad-tech reaches for.

The default allowlist for most of these is permissive. browsing-topics, the directive that controls the Topics API, defaults to * — every origin on the page, including third-party frames, may call it unless you say otherwise. Disabling it is one line:

Permissions-Policy: browsing-topics=()

An empty allowlist () denies the feature to everyone, including your own origin. You can lock down the full cluster of ad-interest and measurement APIs in a single header:

Permissions-Policy: browsing-topics=(), join-ad-interest-group=(), run-ad-auction=(), attribution-reporting=(), shared-storage=(), private-aggregation=()

Any script — first-party or embedded — that calls document.browsingTopics() or sends a Sec-Browsing-Topics request header now fails with a NotAllowedError. You are not trusting third parties to behave; you are removing the capability at the platform level.

These directives are deprecated alongside Privacy Sandbox, so some will disappear from Chrome over coming releases. Setting them to () is harmless once they are gone and protective until then.

Referrer-Policy: Stop Leaking Your URLs

Every navigation and subresource request can carry a Referer header naming the page the user came from. Full URLs leak query strings, path-encoded identifiers, search terms, and internal page structure to every third party you load — analytics vendors, fonts, CDNs, ad pixels.

Modern browsers default to strict-origin-when-cross-origin, which is already a reasonable floor:

Referrer-Policy: strict-origin-when-cross-origin

Under this policy the browser sends the full URL only on same-origin requests, sends the origin only on cross-origin secure requests, and sends nothing when downgrading from HTTPS to HTTP. So https://app.example/orders/4815?token=abc reaches a third party as https://app.example/ — origin, no path, no query.

That default became the spec default in November 2020, but you should still set the header explicitly. A misconfigured upstream, an old framework default, or a <meta> referrer tag can override it, and an explicit header is auditable. If you embed nothing sensitive in URLs and want to be strict, strict-origin drops the path on same-origin requests too.

Why This Pairs With Cookie-Free Analytics

These headers and a privacy-first tracker solve the same problem from two directions. The tracker controls what your analytics collects; the headers control what everyone else on the page can reach.

A cookie-free tracker that derives identity from a one-way daily hash — SHA-256(IP | UA | SALT_SECRET | YYYY-MM-DD) — never needs the full referrer URL or the Topics API to do its job. It reads document.referrer only to attribute a visit source, and the origin is enough for that. So tightening Referrer-Policy costs your analytics nothing while it closes a leak for every other request on the page.

The same alignment holds for performance. Headers cost zero bytes of JavaScript and zero main-thread time; they are evaluated by the browser before parsing. You get a measurable privacy improvement with no impact on Largest Contentful Paint or Interaction to Next Paint — the opposite trade-off from a consent-management platform, which adds script weight to manage permissions a header could have denied outright.

Ship Them at the Edge

Set both headers at your edge or origin server so every response carries them, including static assets and HTML:

Permissions-Policy: browsing-topics=(), join-ad-interest-group=(), run-ad-auction=(), attribution-reporting=(), shared-storage=()
Referrer-Policy: strict-origin-when-cross-origin

On Cloudflare, add them in a Transform Rule or a Worker; in Next.js, the headers() config; in any reverse proxy, a single add_header line. There is no rollout risk: you are denying capabilities you do not use and trimming data you never wanted to send.

Privacy by design is usually framed as an architecture decision. At the HTTP layer it is two lines of configuration, and they default open until you close them.

Sources

Comments

Loading comments…