Permissions Policy Explained

Control which powerful browser APIs can be used by your site and embedded third-party content.

Quick Summary

  • Permissions-Policy (formerly Feature-Policy) controls browser API access.
  • It prevents third-party iframes from accessing camera, geolocation, or microphone.
  • Disabled features cannot be re-enabled by embedded widgets.
  • Empty parentheses () disable a feature globally.

Introduction

Modern browsers expose powerful APIs that allow websites to access the camera, microphone, geolocation, payment systems, USB devices, and more. These APIs are essential for some applications, but they also create significant privacy and security risks when third-party code runs on your page.

The Permissions-Policy header lets you control exactly which browser APIs can be used on your page and by whom. It applies to both your own code and any embedded third-party content. By explicitly disabling APIs you don't use, you shrink your attack surface and protect users from rogue scripts.

This guide explains what Permissions-Policy controls, why it matters for privacy and security, and how to configure it correctly as part of your security headers strategy. Read more on the W3C Permissions Policy specification.

What Is Permissions Policy?

API Access Governance

The Permissions-Policy header (formerly Feature-Policy) lets you control which browser APIs and features can be used on your page. It applies to both your own code and any embedded third-party iframes.

Once a feature is disabled via Permissions-Policy, it cannot be re-enabled by any embedded content, even if the iframe has its own allow attribute. This makes it a hard gate, not a suggestion.

How Directives Work

How Permissions-Policy Gates Browser APIsEmbedded 3rd-party widgetRequests camera / geolocationBrowser checks Permissions-Policycamera=(self) → OKcamera=() → DENIED

As shown above, when a third-party widget requests access to a browser API, the browser checks the Permissions-Policy before showing any permission prompt. If the policy denies the feature, the request is silently blocked, the user never even sees a prompt.

Why It Matters for Privacy

Preventing Abuse

Third-party widgets run in your page's context. This creates a trust exploitation scenario:

  • Trust exploitation: Users see browser permission prompts from your domain, not the widget's. If they grant access, they are unknowingly giving that access to the third-party code.
  • Data collection: A rogue ad widget could silently access geolocation data, revealing the user's physical location to an ad network.
  • Fingerprinting: APIs like battery status, hardware concurrency, and media devices can be used for browser fingerprinting, identifying users without cookies.
  • Payment hijacking: Without restrictions, a compromised widget could access the Payment Request API to display payment sheets that redirect funds.

Trust Exploitation

Third-party widgets run in your page's context. Users see permission prompts from your domain, not the widget's. A Permissions-Policy stops this by denying the API before the prompt appears.

Common Controlled Features

FeatureAPI ControlledRecommendation
cameragetUserMedia (video)Disable unless needed: camera=()
microphonegetUserMedia (audio)Disable unless needed: microphone=()
geolocationNavigator.geolocationDisable unless needed: geolocation=()
paymentPayment Request APIRestrict to self + trusted processor
usbWebUSBDisable: usb=()
fullscreenFullscreen APIRestrict to self: fullscreen=(self)
autoplayMedia autoplayRestrict to self: autoplay=(self)
display-captureScreen capture APIDisable: display-capture=()
batteryBattery Status APIDisable to prevent fingerprinting

Check if your Permissions Policy is properly configured.

Run Security Headers Check

Real-World Examples

  • Ad network geolocation abuse: Several ad networks were found requesting geolocation access through embedded iframes. Users unknowingly granted location access to their domain, allowing the ad network to track their physical movements.
  • Cryptocurrency mining: Malicious iframes used the Web Workers API to run cryptocurrency miners in the background, draining battery and CPU without the user's knowledge.
  • Camera/mic access via chat widgets: Some chat widgets requested camera and microphone access for “video support” features, but the data was accessible to the widget vendor's servers.
  • Fingerprinting via unused APIs: Trackers used APIs like navigator.getBattery() and navigator.hardwareConcurrency purely for fingerprinting purposes, not for any user-facing feature.

How to Check Your Policy

  1. Run a headers scan: Use the Security Headers Checker to see if your Permissions-Policy header is present and review which features are configured.
  2. Check DevTools: Open DevTools → Application → Permissions Policy. Chrome shows which features are enabled, disabled, or set to specific origins.
  3. Test third-party iframes: Load your page with embedded widgets and check if they can access sensitive APIs. Use DevTools Console to test: navigator.geolocation.getCurrentPosition(console.log).
  4. Full privacy audit: Run a comprehensive scan to check Permissions-Policy alongside other security headers.

How to Implement Permissions-Policy

Deployment Techniques

Disable All Sensitive APIs

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), display-capture=()

Empty parentheses () disable the feature for all contexts, including your own page.

Allow Specific Features for Your Domain

Permissions-Policy: geolocation=(self), payment=(self "https://payments.example.com"), camera=(), microphone=()

Geolocation restricted to your origin. Payment API allowed for you and one trusted processor. Camera and mic disabled entirely.

Add the header at your web server or CDN level:

Nginx Configuration

add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
Start by disabling everything you don't need, then selectively enable only what your application requires. This follows the principle of least privilege. Use the Security Headers Checker to verify it's correctly set.

Best Practices

  1. Deny by default, allow by exception, disable all sensitive APIs, then selectively enable only what your application genuinely uses.
  2. Restrict to self for used features, if your app uses geolocation, set geolocation=(self) to prevent third-party iframes from accessing it.
  3. Combine with CSP, Permissions-Policy controls API access, CSP controls content loading. Together they provide comprehensive protection.
  4. Review when adding third-party widgets, every new chat widget, analytics tool, or embedded component could potentially access browser APIs. Check what they need.
  5. Document your feature requirements, maintain a list of which APIs your application uses and why. This helps during security audits and when onboarding new developers.
  6. Test iframe behavior, verify that embedded content cannot access features you have disabled. Use browser DevTools to confirm.

Common Mistakes

  • Not setting Permissions-Policy at all: Without the header, all browser APIs are available to all content on the page, including third-party iframes.
  • Using the deprecated Feature-Policy syntax: The old Feature-Policy header uses different syntax. Use Permissions-Policy with the modern Structured Field Values format.
  • Enabling features for all origins: camera=* allows all embedded content to access the camera. Use specific origins instead: camera=(self).
  • Only setting Allow attributes on iframes: The allow attribute on an iframe cannot grant permissions that the page-level Permissions-Policy has denied. You need both.
  • Forgetting about new APIs: Browsers regularly add new APIs (Serial, Bluetooth, HID). Review your policy when browsers release new versions to disable features you do not need.

Conclusion

Permissions-Policy is a simple but powerful header that prevents third-party code from accessing sensitive browser APIs on your behalf. It takes a single line of server configuration to disable camera, microphone, and geolocation access for all embedded content, closing an entire category of privacy and security risks.

Combined with a strong Content Security Policy and other security headers, Permissions-Policy creates a robust defense that protects users even when third-party code behaves maliciously.

Scan Your Website

Scan your website with SitePrivacyScore to detect Permissions-Policy issues automatically. Our free scanner checks which browser APIs are exposed to third-party content.

Related Guides

Frequently Asked Questions

What is the difference between Feature-Policy and Permissions-Policy?+
Feature-Policy was the original name. It has been renamed to Permissions-Policy with updated syntax using Structured Field Values. Use the new header, but some sites still send the deprecated one for legacy support.
How does Permissions-Policy interact with iframe allow attributes?+
The HTTP header sets the maximum allowed permissions for the page. An iframe allow attribute cannot grant permissions beyond what the header allows.
Should I disable all features by default?+
Disable features you don't use (camera, microphone, geolocation). Only enable features your application actually requires, restricted to your own origin where possible.
Does Permissions-Policy affect performance?+
No direct performance impact, but it prevents third-party scripts from triggering resource-intensive APIs like geolocation or camera, which can indirectly improve battery and performance.
Can Permissions-Policy prevent fingerprinting?+
Partially. Disabling APIs like battery status (navigator.getBattery) and WebUSB reduces the data points available for browser fingerprinting.
Is Permissions-Policy supported in all browsers?+
Chrome, Edge, and Firefox support it. Safari has partial support. Always check compatibility for specific features you want to control.

Scan for Security Vulnerabilities

Ensure you aren't leaving powerful browser APIs open to exploitation by third-party tracking scripts.

For deeper runtime checks, run the full privacy audit →