Permissions-Policy
Permissions-Policy lets you turn off powerful browser features — camera, microphone, geolocation, payment, USB — for your own pages and for any iframes you embed.
What it is
Permissions-Policy is the response header that succeeds the older Feature-Policy. It tells the browser which powerful features may be used by the current document and by any documents embedded in iframes. Features include camera, microphone, geolocation, payment, USB, fullscreen, autoplay, accelerometer, and many others.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
An empty allow-list () disables the feature entirely. (self) allows it on the current origin. (self "https://embed.example.com") allows it on the current origin and one named third party.
Why it matters
The default state of the web grants pages access to many powerful APIs as soon as the user accepts a prompt. If you do not use the camera on your marketing site, there is no reason for an embedded ad iframe to be able to ask for it. Permissions-Policy lets you turn off features you do not need, both for your own code (defence against XSS expanding into hardware access) and for embedded third parties (defence against ad networks and trackers).
It is also a useful privacy signal: a site that explicitly disables geolocation is announcing it does not collect location data.
How to implement
Send the header on every HTML response. Start by denying everything you do not use:
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(self), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()
Then enable only the features you actually need, scoped as narrowly as possible:
Permissions-Policy: camera=(self), microphone=(self), geolocation=(self), fullscreen=(self)
To allow a specific third party — say, a video embed:
Permissions-Policy: fullscreen=(self "https://player.vimeo.com"), autoplay=(self "https://player.vimeo.com")
You can also constrain a single iframe with the allow attribute:
<iframe src="https://player.vimeo.com/video/123" allow="fullscreen; autoplay"></iframe>
The iframe’s effective permissions are the intersection of the page policy and the allow attribute.
Common mistakes
- Using the older
Feature-Policysyntax. Permissions-Policy uses structured headers with()lists, not space-separated origins. - Forgetting to allow features you actually use. If
camera=()is set, your own WebRTC code stops working. - Allowing
*to “fix” a broken iframe. Narrow it to the specific origin instead. - Setting the header on the API but not the HTML. Only the top-level HTML response counts.
Verification
curl -sI https://example.com | grep -i permissions-policyshould print the header.- Visit permissionspolicy.com or use Chrome DevTools → Application → Frames → Permissions Policy to see the effective policy.
- Try
navigator.mediaDevices.getUserMedia({camera: true})from the console on a page where camera is denied — it should reject with a permissions error. - Audit embedded iframes; check that their
allowattributes match what they actually need.
Related topics
Sources & further reading
- Permissions Policy (W3C Working Draft) — W3C
- MDN — Permissions-Policy — MDN
- web.dev — Permissions Policy — Chrome for Developers