Dr.Who
← blog

Permissions-Policy: disable the browser features your site never uses

· permissions-policy · security-headers · feature-policy · browser-features · privacy

permissions-policysecurity-headersfeature-policybrowser-featuresprivacy

The Permissions-Policy response header lets a site declare which powerful browser features — camera, microphone, geolocation, payment, and others — the page and any iframes it embeds are allowed to use. It replaced the older Feature-Policy header (same idea, new name and slightly different syntax). A practical baseline is to disable every feature your site does not actually call, which shrinks the surface a compromised script or a sketchy embedded iframe can abuse. It is a hardening control, not an authentication or content-injection defence — that is still CSP's job.

A recommended baseline

If your site does not use the camera, microphone, or location, turn them off explicitly. An empty allowlist () means "no origin, not even this page":

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()

That header denies those features to your own page and to every iframe. interest-cohort=() opts the page out of the legacy FLoC trial; it is harmless to keep even though FLoC itself was discontinued. Add or remove features to match what your application genuinely needs — there is no benefit to listing features you do not control.

If a feature is needed, allow only the origin that needs it rather than leaving it open. A video-call page that uses the camera and mic on its own origin would write:

Permissions-Policy: camera=(self), microphone=(self), geolocation=()

The allowlist syntax

Each directive is feature=<allowlist>. The allowlist is a space-separated list inside parentheses:

  • () — empty list. The feature is disabled for everyone, including this page.
  • (self) — the feature is allowed for the current origin only. Same-origin iframes inherit it; cross-origin ones do not.
  • * — allowed for all origins (this page and any iframe). Use sparingly.
  • (self "https://embed.example.com") — allowed for this origin plus the named cross-origin embed. Origins are quoted and space-separated.

To let a specific third-party iframe use a feature, name its origin in the parent's header and grant it on the iframe element with the allow attribute, e.g. allow="camera". Both the document policy and the iframe attribute must permit it.

What it does and does not protect

Permissions-Policy reduces abuse and accidental exposure: a third-party tag cannot silently prompt for geolocation, an embedded widget cannot reach for the microphone, and a compromised dependency loses access to capabilities you never enabled. That is real defence-in-depth.

It is not a substitute for a Content-Security-Policy. CSP governs what code and content can load and execute; Permissions-Policy governs which device and browser capabilities that code may reach once it is running. They cover different layers — ship both. The header is also not enforced retroactively on already-granted permissions in some edge cases, so treat it as a default-deny posture you set from the start.

Check your security headers →

Further reading