Permissions Policy Constrains Browser Feature Access
A web document can contain first-party code, third-party scripts, and embedded frames that execute within different origins. Browser APIs then add another boundary: some features expose sensors, media devices, display state, or other capabilities that a site may not want every embedded context to use.
Permissions-Policy lets a response declare which origins may use selected browser features in the document and its descendants. The policy is a capability boundary, not a replacement for the permission prompt shown to a person. A feature can be permitted by policy and still be denied by browser permission state, platform settings, secure-context requirements, or other API-specific conditions.
A response can remove capabilities from the document tree
A restrictive policy can be sent as an HTTP response header:
Permissions-Policy: geolocation=(self), camera=(), microphone=()Here, geolocation is allowed for the response origin through self. The empty allowlists for camera and microphone disable those features for the document and frames governed by that policy.
This is useful as a negative security boundary. An application that never needs camera access can deny it at the document level instead of relying on every script and embedded component to avoid the API.
Feature names and browser support are not uniform across every implementation. A deployment should base its policy on features supported by its target browsers and test the resulting behavior rather than treating an unknown directive as enforcement.
Allowing a feature does not grant user permission
Permissions Policy and browser permission state answer different questions.
The policy determines whether a document is eligible to use a controlled feature. A permission system can separately require a user decision before the API returns sensitive data or activates a device.
feature request
|
v
Permissions Policy permits it?
|
+-- no -> blocked by policy
|
`-- yes
|
v
API-specific checks
permission state
secure context
platform conditionsA permissive policy therefore does not silently approve camera, microphone, or geolocation access. Conversely, a previously granted browser permission does not let a frame bypass a policy that disables the feature for that context.
Keeping those layers separate matters during incident analysis. A permission prompt is not evidence that every embedding relationship is appropriately scoped, and a restrictive header is not evidence that a user has granted an API permission.
Frame delegation is explicit
Embedded content often needs a narrower set of capabilities than the top-level application. An iframe can carry an allow attribute that controls feature access for that frame:
<iframe
src="https://maps.example/widget"
allow="geolocation 'self'">
</iframe>The frame policy operates within the limits inherited from its ancestors. An iframe attribute cannot restore a capability that an enclosing Permissions Policy has already removed.
That ordering prevents a child declaration from expanding authority beyond the boundary established above it. Delegation is therefore most useful when the top-level policy permits a capability for a deliberate embedding case and the frame declaration narrows where that capability is available.
Cross-origin frames deserve particular care. A widget that needs a controlled feature should receive only the feature and origin scope required by the integration rather than a broad set of unrelated capabilities.
Default allowlists differ by feature
Permissions Policy does not have one universal default for every controlled feature. Each feature has a defined default allowlist, and those defaults affect behavior when the response omits a directive.
This makes an omitted directive different from an explicit empty allowlist.
Permissions-Policy: camera=()The line above states an intentional denial for camera. Omitting camera delegates behavior to that feature’s default and the surrounding policy state.
Security-sensitive applications benefit from explicit directives for capabilities they intend to deny. Explicit policy also makes review easier because the desired boundary is visible in the response configuration rather than inferred from a browser default that can differ across features.
Ancestor policy limits descendants
Policy inheritance matters when frames are nested. A top-level document can constrain a feature before a descendant frame applies its own declaration.
Consider this hierarchy:
app.example
|
+-- partner.example
|
`-- tool.exampleIf the top-level policy disables a controlled feature for the partner frame, a deeper frame cannot re-enable that feature merely by requesting it in its own allow attribute.
This property gives the embedding application a meaningful upper bound on delegated browser capabilities. It also means debugging must inspect the full ancestor chain. A frame can appear correctly configured locally while remaining ineligible because a parent established a stricter policy.
Policy should follow the actual embedding graph
A header copied from another application can be either too broad or unexpectedly disruptive. The useful policy starts from the site’s real frame graph and capability requirements.
For each controlled feature, the relevant questions are concrete: does the top-level application use it, does an embedded origin require it, and which ancestor is responsible for delegation? The resulting allowlist can then reflect those relationships directly.
A site with no legitimate microphone use can state:
Permissions-Policy: microphone=()A site that uses geolocation only in its own origin can state:
Permissions-Policy: geolocation=(self)More complex delegation should be tested with the exact origins and navigation paths used in production. Redirects, alternate hosts, sandboxed frames, and nested embeds can change the context in which a policy is evaluated.
Console diagnostics are evidence, not enforcement
Browsers can report policy violations or blocked feature use through developer tooling. Those messages are useful when a new policy breaks an integration, especially when the blocked call occurs inside a third-party frame.
Diagnostics should not become the security control itself. Production enforcement comes from the policy delivered with the document and the browser’s implementation of the controlled feature. Console output is an observation surface for validating that boundary.
Automated browser tests can complement manual inspection by exercising pages that are expected to use or reject specific capabilities. Tests should cover both permitted and denied paths, including nested frames where delegation is part of the design.
Keep the boundary narrow and testable
Permissions Policy is strongest when it expresses a small, deliberate capability map. Disable features the application does not need, scope required features to the origins that need them, and keep iframe delegation aligned with the response policy.
The header does not authenticate embedded content, sanitize script, enforce application authorization, or replace Content Security Policy. It controls a separate browser boundary: eligibility to invoke selected features from a particular document context.
Treating that boundary independently makes configuration easier to audit. A document can then expose only the browser capabilities required by its embedding graph while leaving user permission, origin checks, application authorization, and content controls to their respective layers.