Documentation

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

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
Browser limitation: Firefox and Safari do not support the 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:

HTTP Define the reporting endpoint
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:

HTTP Report-Only mode for safe testing
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.

Start with Report-Only: Always start with 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:

HTTP Common Permissions Policy configuration
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 origins
  • microphone=() — Disables microphone access for all origins
  • geolocation=(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:

HTTP Enforced Permissions Policy with reporting
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.

Keep reporting enabled: Continue monitoring even after enforcement. New third-party scripts or updates to existing ones may introduce new feature access attempts. Ongoing reports help you maintain governance.

Step 6: Set Up Integrations

Route Permissions Policy violation reports to your existing tools:

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

JSON Permissions Policy violation (geolocation blocked)
{
  "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:

HTTP Multiple directives
Permissions-Policy: camera=(), microphone=(), geolocation=(self "https://maps.example.com")
URL quoting: URLs must be quoted with double quotes in the header value. Single quotes do not work: 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:

HTML Iframe 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 allow attribute is present, the iframe inherits from the header policy
Iframe attribute syntax differs: The iframe 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

Nginx nginx.conf
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

Ruby config/application.rb or initializer
# 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

Apache .htaccess or httpd.conf
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 (or Permissions-Policy-Report-Only) and Reporting-Endpoints headers are present.
  • Check report-to directive — The policy must include report-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 EnforcePermissions-Policy-Report-Only sends reports but doesn't block. Switch to Permissions-Policy to 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 allow attribute, 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-to directive 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