Permissions-Policy

Unrestricted access to browser features like camera, microphone, and geolocation creates privacy and security risks. The Permissions-Policy response header controls which features a page and its embedded iframes are allowed to use.

Baseline: Limited availability

Chromium-based browsers and Firefox support the Permissions-Policy header. Safari recognizes the legacy Feature-Policy name for some directives but does not implement Permissions-Policy syntax. webstatus.dev

Usage

The Permissions-Policy response header restricts which browser features are allowed in the current frame. Each policy pairs a feature directive with an allowlist defining which Origins access the feature.

Permissions-Policy: <directive>=<allowlist>

Note

A recommended starting policy disables features the site does not use: Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=(). Security scanners flag missing Permissions-Policy headers. Setting a minimal policy resolves the finding without affecting site functionality.

Note

The Permissions-Policy mechanism was previously known as Feature Policy.

Directives

The directive name identifies the browser feature to control. A non-exhaustive list of standardized directives:

  • accelerometer: device motion sensor
  • autoplay: media autoplay
  • camera: video capture
  • encrypted-media: Encrypted Media Extensions
  • fullscreen: fullscreen mode
  • geolocation: location access
  • gyroscope: orientation sensor
  • magnetometer: magnetic field sensor
  • microphone: audio capture
  • midi: Web MIDI API
  • payment: Payment Request API
  • picture-in-picture: PiP mode
  • sync-xhr: synchronous XMLHttpRequest
  • unload: unload event handlers
  • usb: WebUSB API

Note

The interest-cohort directive was used to block Federated Learning of Cohorts (FLoC). Google abandoned FLoC in 2022 and replaced the API with the Topics API. The interest-cohort directive has been removed from browser implementations.

Allowlist values

The <allowlist> controls which origins use the feature:

  • *: all origins
  • (): disabled entirely
  • self: same origin only
  • "https://example.re": specific origin
  • (self "https://a.example.re"): multiple origins

Note

The unload directive blocks unload event handlers for the origin. Setting Permissions-Policy: unload=() prevents first-party code, third-party scripts, and browser extensions from registering unload handlers. This is significant for back-forward cache (bfcache) performance because unload handlers prevent the browser from caching pages for instant back and forward navigation. Chrome is gradually deprecating unload events entirely, beginning with high-traffic sites and expanding to all sites. Setting unload=() opts into the deprecation immediately and guarantees bfcache eligibility regardless of the rollout schedule.

Example

In the following example, the server indicates the geolocation feature shall be disabled in all contexts.

Permissions-Policy: geolocation=()

In the following example, the server indicates the encrypted-media feature shall be disabled for all Origins except for report.example.re.

Permissions-Policy: encrypted-media=("https://report.example.re")

In the following example, the server indicates the microphone feature shall be disabled for all Origins except itself and those with origin example.re.

Permissions-Policy: microphone=(self "https://example.re")

Blocking unload handlers to improve bfcache eligibility. This prevents third-party scripts and extensions from registering unload events degrading back-forward navigation performance.

Permissions-Policy: unload=()

Troubleshooting

Permissions-Policy violations appear silently in most cases, with the blocked feature returning a denied state or throwing an error without a visible browser prompt.

  1. Feature blocked in an iframe due to missing allow attribute. Even when the parent page permits a feature, embedded iframes need an explicit allow attribute to access the feature. Add the attribute to the iframe tag: <iframe src="..." allow="camera; microphone">. Without the attribute, the iframe receives a denied permission state regardless of the HTTP header policy.

  2. Geolocation, camera, or microphone not working after setting the policy. A policy like Permissions-Policy: geolocation=() disables the feature for all origins, including the page itself. To allow the feature on the same origin only, use geolocation=(self). To allow a specific embedded origin as well, use geolocation=(self "https://maps.example.re"). The browser never prompts the user for a feature the policy has already denied.

  3. Wildcard * not working for all features. Some features do not support the * allowlist value and are restricted to explicit origin lists. When * has no effect, specify each allowed origin individually. Check the browser console for warnings about unrecognized or unsupported allowlist values.

  4. Migrating from Feature-Policy to Permissions-Policy. The legacy Feature-Policy header uses a different syntax: Feature-Policy: geolocation 'self' vs. Permissions-Policy: geolocation=(self). Browsers that support Permissions-Policy ignore Feature-Policy when both headers are present. Update the header name and convert the syntax: replace spaces with =, wrap origin lists in parentheses, and drop the single quotes around self and none (use () instead of 'none').

  5. Debugging with document.featurePolicy.allowsFeature(). In Chromium-based browsers, open the DevTools Console and run document.featurePolicy.allowsFeature('camera') to check whether a feature is allowed in the current context. This returns true or false. For iframe contexts, pass the origin as a second argument: document.featurePolicy.allowsFeature('camera', 'https://embed.example.re'). Firefox uses document.permissionsPolicy instead of document.featurePolicy.

See also

Last updated: April 4, 2026