Permissions Policy
Configure Permissions Policy reporting to control browser feature access and detect when third-party scripts attempt to use sensitive APIs like camera, microphone, geolocation, and payment.
New to Permissions Policy monitoring? Read our Permissions Policy overview to understand the benefits before diving into configuration.
What is Permissions Policy?
Permissions Policy is a W3C standard that lets you control which browser features your site and embedded third-party content can access. Using HTTP headers, you define an allowlist of origins permitted to use powerful APIs like camera, microphone, geolocation, and payment requests.
When a script attempts to use a feature that violates your policy, the browser blocks the access and can send a violation report. This gives you visibility into which third-party scripts are attempting to access sensitive APIs — critical for privacy compliance and vendor governance.
Permissions Policy evolved from the older Feature Policy specification. While the underlying
concepts are similar, Permissions Policy uses a newer syntax based on
Structured Headers (RFC 8941).
Legacy Feature-Policy headers may still work in some browsers but should be migrated to the modern
Permissions-Policy header.
Specification
- W3C Permissions Policy — Official specification
- MDN: Permissions Policy — Developer documentation
- MDN: Permissions-Policy Header — Header reference
Browser Support
Permissions Policy has ~77% global browser support, covering Chromium-based browsers. Firefox and Safari have limited or no support for the HTTP header.
| Browser | Support | Notes |
|---|---|---|
| Chrome | 88+ | Full support including reporting |
| Edge | 88+ | Full support including reporting |
| Opera | 74+ | Full support |
| Firefox | — | Not supported (behind experimental flag) |
| Safari | — | Not supported for HTTP header |
Permissions-Policy HTTP header. Violation reports will only come
from Chromium-based browsers (~77% of users). However, the <iframe allow="..."> attribute has
broader support.
Why Monitor Permissions Policy Violations?
Permissions Policy violations reveal important information about third-party behavior:
- Privacy compliance evidence — Create audit trails showing that unauthorized feature access attempts were blocked. Demonstrate GDPR/CCPA compliance to regulators.
- Third-party governance — Identify which vendor scripts are attempting to access sensitive APIs like camera, microphone, and geolocation. Make informed decisions about vendor relationships.
- Policy testing — Use Report-Only mode to test restrictive policies before enforcement. See what would break without affecting users.
- Security monitoring — Detect unexpected feature access attempts that could indicate compromised scripts or malicious code.
- Feature auditing — Understand which browser features your site and third parties actually use vs. what you've allowed.
Who Benefits
- Privacy officers (DPO) — Demonstrate compliance with privacy regulations and governance over third-party data collection
- Security engineers — Monitor and restrict access to sensitive browser APIs across the site
- Compliance teams — Generate audit logs proving that privacy policies are enforced
- Frontend architects — Understand feature usage across the codebase and third-party integrations
When to Enable
Permissions Policy monitoring is recommended for:
- Privacy-sensitive sites — Healthcare, finance, and sites handling personal data where feature access must be documented
- Sites with third-party scripts — Analytics, advertising, and widgets that might access sensitive APIs
- Compliance requirements — When GDPR, CCPA, or other regulations require proof of data access controls
- Security-conscious applications — When you want to detect and respond to unauthorized feature access attempts
Start with Report-Only mode to understand your current exposure before enforcing restrictions.
How to Configure
Step 1: Set Up the Reporting Endpoint
First, define where browsers should send violation reports using the Reporting-Endpoints header:
Reporting-Endpoints: default="https://reporting-api.app/browser-reports/YOUR-ENDPOINT-UUID"
Replace YOUR-ENDPOINT-UUID with your application's unique endpoint from the
reporting-api.app dashboard.
Step 2: Enable Report-Only Mode for Testing
Before enforcing your policy, test it using Permissions-Policy-Report-Only. This sends violation reports
without blocking any features, letting you see what would break:
Permissions-Policy-Report-Only: camera=(), microphone=(), geolocation=(self); report-to="default" Reporting-Endpoints: default="https://reporting-api.app/browser-reports/YOUR-UUID"
This configuration would report (but not block) any script attempting to use camera or microphone, while only allowing geolocation for same-origin scripts.
Permissions-Policy-Report-Only before enforcing policies. Third-party scripts may have
legitimate reasons to access features you didn't know about. Report-Only mode reveals these dependencies without
breaking functionality.
Step 3: Configure Permission Directives
Define which features to control. Each directive specifies which origins can use that feature:
Permissions-Policy-Report-Only: camera=(), microphone=(), geolocation=(self), payment=(self "https://payments.example.com"), fullscreen=(self); report-to="default"
This policy:
camera=()— Disables camera access for all originsmicrophone=()— Disables microphone access for all originsgeolocation=(self)— Allows geolocation only for same-origin scripts-
payment=(self "https://payments.example.com")— Allows payment API for self and a specific payment provider fullscreen=(self)— Allows fullscreen only for same-origin scripts
Step 4: Monitor Reports and Identify Violations
After deploying Report-Only mode, monitor incoming reports in your dashboard. Look for:
- Third-party violations — Scripts from analytics, advertising, or widget providers attempting blocked features
- Your own code — Features your application uses that you need to explicitly allow
- Unexpected access patterns — Scripts accessing features you didn't expect
For each violation, decide whether to:
- Allow the feature — Add the origin to your allowlist
- Block and accept — The feature isn't needed; proceed with enforcement
- Remove the script — The vendor's behavior is unacceptable
Step 5: Switch to Enforcement
Once you've resolved all issues, switch from Report-Only to enforced mode:
Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=(self "https://payments.example.com"), fullscreen=(self); report-to="default" Reporting-Endpoints: default="https://reporting-api.app/browser-reports/YOUR-UUID"
Remove the -Report-Only suffix to start blocking unauthorized feature access. Keep
report-to="default" to continue receiving reports about blocked attempts.
Step 6: Set Up Integrations
Route Permissions Policy violation reports to your existing tools:
- AppSignal integration — Track violations alongside application errors
- Webhook integration — Send reports to compliance systems or SIEM tools
- Google Chat integration — Post alerts to team chat spaces
See the Integrations documentation for setup instructions.
Understanding Violation Reports
When a script violates your Permissions Policy, browsers send JSON reports to your endpoint. These reports identify exactly which feature was blocked and where the violation occurred.
Report Fields
| Field | Description |
|---|---|
featureId |
The feature that was blocked (e.g., geolocation, camera, microphone)
|
disposition |
enforce if blocked, report if Report-Only mode
|
message |
Human-readable description of the violation |
sourceFile |
URL of the script that attempted the feature access |
lineNumber |
Line number where the violation occurred |
columnNumber |
Column number where the violation occurred |
Example Report
{
"type": "permissions-policy-violation",
"age": 15,
"url": "https://example.com/checkout",
"body": {
"featureId": "geolocation",
"disposition": "enforce",
"message": "Permissions policy violation: geolocation is not allowed in this document.",
"sourceFile": "https://cdn.analytics-vendor.com/tracker.js",
"lineNumber": 847,
"columnNumber": 23
}
}
This report shows an analytics vendor script attempting to access geolocation on the checkout page. The
disposition: "enforce" indicates the access was blocked (not just reported).
Interpreting Disposition
-
report— Report-Only mode. The feature access was allowed but logged. Use this for testing. -
enforce— Enforcement mode. The feature access was blocked. The script's API call failed.
Common Permission Directives
Permissions Policy supports over 40 directives. Here are the most commonly restricted features:
Sensitive Data Access
| Directive | Controls | Privacy Impact |
|---|---|---|
camera |
Video input devices | High - visual surveillance |
microphone |
Audio input devices | High - audio surveillance |
geolocation |
Geographic location | High - physical location tracking |
payment |
Payment Request API | High - financial data access |
display-capture |
Screen capture | High - can capture sensitive content |
Media Controls
| Directive | Controls | Use Case |
|---|---|---|
autoplay |
Auto-playing media | Block unwanted video/audio autoplay |
fullscreen |
Fullscreen mode | Prevent fullscreen takeover |
picture-in-picture |
Picture-in-picture mode | Control floating video windows |
Sensor Access
| Directive | Controls | Privacy Impact |
|---|---|---|
accelerometer |
Motion sensor | Can infer user activity |
gyroscope |
Orientation sensor | Can infer device orientation |
magnetometer |
Compass sensor | Can approximate location |
ambient-light-sensor |
Light sensor | Can infer environment |
Hardware & Modern APIs
| Directive | Controls | Privacy Impact |
|---|---|---|
usb |
WebUSB API | Device access |
bluetooth |
Web Bluetooth API | Peripheral access |
hid |
WebHID API | Input device access |
serial |
Web Serial API | Serial port access |
screen-wake-lock |
Wake Lock API | Battery usage |
For a complete list, see the MDN Permissions-Policy directives reference.
Directive Syntax
Permissions Policy uses Structured Headers syntax. Each directive specifies which origins can use a feature:
| Syntax | Meaning | Example |
|---|---|---|
feature=() |
Disabled for all origins | camera=() |
feature=(self) |
Allowed for same origin only | geolocation=(self) |
feature=(self "URL") |
Allowed for self and specific origin | payment=(self "https://pay.example.com") |
feature=* |
Allowed for all origins (default) | fullscreen=* |
Multiple directives are combined with commas:
Permissions-Policy: camera=(), microphone=(), geolocation=(self "https://maps.example.com")
geolocation=(self "https://example.com") is correct,
geolocation=(self 'https://example.com') is invalid.
Iframe Allow Attribute
In addition to the HTTP header, you can control feature access for specific iframes using the allow
attribute:
<!-- Allow only geolocation for this iframe --> <iframe src="https://maps.example.com/embed" allow="geolocation 'self' https://maps.example.com" ></iframe> <!-- Block all sensitive features for this iframe --> <iframe src="https://ads.example.com/banner" allow="camera 'none'; microphone 'none'; geolocation 'none'" ></iframe>
Relationship with HTTP Header
The iframe allow attribute and HTTP header work together with AND logic:
- A feature must be allowed by both the HTTP header and the iframe attribute
- The iframe attribute can only restrict further — it cannot enable features blocked by the header
- If no
allowattribute is present, the iframe inherits from the header policy
allow attribute uses the older Feature Policy syntax with single quotes:
allow="geolocation 'self'". The HTTP header uses Structured Headers syntax with double quotes:
geolocation=(self). Don't mix them up.
Server Configuration Examples
Nginx
server {
# ... your existing configuration ...
# Reporting endpoint
add_header Reporting-Endpoints 'default="https://reporting-api.app/browser-reports/YOUR-UUID"' always;
# Permissions Policy with reporting (Report-Only for testing)
add_header Permissions-Policy-Report-Only 'camera=(), microphone=(), geolocation=(self); report-to="default"' always;
# Or enforced mode:
# add_header Permissions-Policy 'camera=(), microphone=(), geolocation=(self); report-to="default"' always;
}
Ruby on Rails
# Add Permissions Policy headers with reporting # Note: Use lowercase header names for Rack 3 compatibility config.action_dispatch.default_headers.merge!( "reporting-endpoints" => 'default="https://reporting-api.app/browser-reports/YOUR-UUID"', # Report-Only for testing: "permissions-policy-report-only" => 'camera=(), microphone=(), geolocation=(self); report-to="default"' # Or enforced mode: # "permissions-policy" => 'camera=(), microphone=(), geolocation=(self); report-to="default"' )
Apache
Header always set Reporting-Endpoints 'default="https://reporting-api.app/browser-reports/YOUR-UUID"' # Report-Only for testing: Header always set Permissions-Policy-Report-Only 'camera=(), microphone=(), geolocation=(self); report-to="default"' # Or enforced mode: # Header always set Permissions-Policy 'camera=(), microphone=(), geolocation=(self); report-to="default"'
Troubleshooting
Reports Not Appearing
- Check browser support — Permissions Policy reporting only works in Chromium-based browsers (Chrome 88+, Edge 88+). Firefox and Safari do not support this feature.
-
Verify headers are sent — Use browser DevTools (Network tab) to confirm both
Permissions-Policy(orPermissions-Policy-Report-Only) andReporting-Endpointsheaders are present. -
Check
report-todirective — The policy must includereport-to="default"to send reports. - Check allowed origins — Ensure your website's origin is whitelisted in your application settings.
- Trigger a violation — Ensure a script is actually attempting to use a blocked feature.
Third-Party Scripts Still Accessing Features
-
Check Report-Only vs Enforce —
Permissions-Policy-Report-Onlysends reports but doesn't block. Switch toPermissions-Policyto enforce. - Verify syntax — Malformed headers may be silently ignored. Use browser DevTools to check for parsing errors.
-
Check iframe allow attribute — If the feature is allowed in an iframe
allowattribute, it may override your expectations.
Policy Syntax Errors
-
Quote URLs correctly — Use double quotes around URLs:
geolocation=(self "https://example.com") - Use correct separators — Separate directives with commas, separate values within a directive with spaces
-
No trailing semicolon — The
report-todirective comes after a semicolon, but the policy itself shouldn't end with one
Feature Works Despite Policy
-
Check same-origin — If the script is same-origin and you allow
self, access is permitted - Check ancestor policies — If your page is embedded in an iframe, the parent's policy may allow the feature
-
Browser defaults — Some features default to
*(allowed for all) if not explicitly restricted
Next Steps
- Integrations — Route reports to your observability tools
- Cross-Origin Isolation — Enable SharedArrayBuffer with COOP/COEP
- Getting Started — Set up your first application