Permissions-Policy Header Explained

Permissions-Policy controls which browser features and APIs are available to your page and to embedded iframes. It replaces the older Feature-Policy header and provides fine-grained control over device access.


The Permissions-Policy HTTP response header lets you control which browser features and APIs are available on your page and — crucially — in any embedded iframes. It prevents malicious or compromised third-party scripts and embeds from accessing sensitive device capabilities such as the camera, microphone, or geolocation without your explicit permission.

What Problem Does It Solve?

Modern browsers expose powerful APIs to web pages: access to the camera and microphone, GPS location, device sensors, payment interfaces, and more. By default, a page can request any of these features, and so can any iframe embedded in it.

Consider an e-commerce site that embeds a third-party chat widget. Without Permissions-Policy, that widget could — if malicious or compromised — attempt to access the user's microphone or camera. Permissions-Policy lets you declare upfront that no iframe on your page should ever access those APIs, regardless of what the embed requests.

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

Relationship to Feature-Policy

Permissions-Policy is the standardised successor to the earlier Feature-Policy header. The rename happened in 2020 alongside a syntax change. If your site still sendsFeature-Policy, replace it with Permissions-Policy — modern browsers have dropped or will drop support for the old name.

Old (Feature-Policy)New (Permissions-Policy)
Feature-Policy: geolocation 'none'Permissions-Policy: geolocation=()
Feature-Policy: camera 'self'Permissions-Policy: camera=(self)
Feature-Policy: microphone *Permissions-Policy: microphone=*

Header Syntax

The header uses a structured field format. Each feature is listed with its allowlist:

http
Permissions-Policy: feature1=(allowlist), feature2=(allowlist), ...

Allowlist Values

AllowlistMeaning
*Any origin — the feature is allowed for the page and all iframes.
(self)Same origin only — the feature is allowed for the page itself but not cross-origin iframes.
()Nobody — the feature is blocked for the page and all iframes.
("https://example.com")Specific origin — allowed only for that origin and the page itself.
(self "https://widget.example.com")Same origin plus a specific third-party origin.

Common Features

FeatureWhat It ControlsRecommended Default
cameraAccess to device cameras via getUserMedia() unless your site needs it
microphoneAccess to microphones via getUserMedia() unless your site needs it
geolocationAccess to the Geolocation API() or (self)
fullscreenUse of the Fullscreen API(self)
paymentAccess to the Payment Request API() or (self)
autoplayAutoplay of audio and video(self)
usbAccess to USB devices via WebUSB()
bluetoothAccess to Bluetooth devices()
accelerometerAccess to the device accelerometer sensor()
gyroscopeAccess to the device gyroscope sensor()
magnetometerAccess to the device magnetometer sensor()
midiAccess to MIDI devices()
picture-in-pictureUse of the Picture-in-Picture API for video(self)
screen-wake-lockPrevent the screen from sleeping(self)
display-captureScreen capture via getDisplayMedia()
xr-spatial-trackingWebXR device access()

Recommended Starting Policy

For most websites that do not need device access, a conservative starting policy blocks all sensitive features:

http
Permissions-Policy: camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=(), autoplay=(self), usb=(), bluetooth=(), accelerometer=(), gyroscope=(), magnetometer=(), midi=(), display-capture=(), xr-spatial-tracking=()
Allow Only What You NeedStart with everything blocked, then enable features selectively as your application genuinely requires them. This follows the principle of least privilege and makes your intent explicit to security auditors.

Controlling iframe Permissions

Permissions-Policy works in combination with the allow attribute on individual <iframe> elements. The page-level policy sets the maximum possible permissions; the iframe's allow attribute can further restrict (but never expand) what the embedded document receives.

html
<!-- Grant geolocation to a specific embed only --> <iframe src="https://maps.example.com/widget" allow="geolocation 'self' https://maps.example.com"> </iframe>
Delegation Cannot Exceed the Page PolicyIf your Permissions-Policy header blocks geolocation entirely with geolocation=(), adding allow="geolocation" to an iframe will have no effect. The header takes precedence.

Server Configuration

Nginx

nginx
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()" always;

Apache

apache
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()"

IIS (web.config)

xml
<system.webServer> <httpProtocol> <customHeaders> <add name="Permissions-Policy" value="camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()" /> </customHeaders> </httpProtocol> </system.webServer>

Caddy

Command
header Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()"

Next.js

javascript
// next.config.mjs const nextConfig = { async headers() { return [ { source: '/(.*)', headers: [ { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()', }, ], }, ]; }, }; export default nextConfig;

Frequently Asked Questions

Does Permissions-Policy replace the iframe sandbox attribute?

They complement each other. The sandbox attribute restricts the iframe's scripting and navigation capabilities, while Permissions-Policy controls access to specific browser features and APIs. Both should be used together for maximum isolation of untrusted embeds.

What happens if I block a feature the page legitimately needs?

Calling a blocked API will result in a SecurityError or a permission denied response from the browser, depending on the API. Users will not see the browser's normal permission prompt for blocked features — the request is rejected before it reaches that stage.

Is Permissions-Policy supported in all browsers?

Permissions-Policy is supported in Chrome, Edge, and Opera. Firefox and Safari have partial support — some features are recognised, others are not. The older Feature-Policy header had different syntax and is being phased out. Where a feature is unsupported, the directive is simply ignored and the browser's default behaviour applies.

Should I combine Permissions-Policy with CSP?

Yes. Permissions-Policy controls device and browser API access; Content Security Policy controls resource loading (scripts, styles, images). They address different threat vectors and complement each other. Both should be in your security headers configuration.

Related Articles