Permissions-Policy Narrows Browser Feature Access

A web document can sit close to powerful browser capabilities. Depending on browser support, context, user permission, and policy, code may request access to features such as geolocation, camera, microphone, or fullscreen. An embedded document can also inherit access to some features from the page that contains it.

Permissions-Policy adds a server-controlled restriction layer. A response can declare which origins are eligible to use selected features in a document and its frame tree. The policy does not grant a user permission and does not make an origin trustworthy. It removes capability from contexts that do not need it.

Policy is an upper bound, not a grant

A response header can restrict a feature to the document’s own origin:

Permissions-Policy: geolocation=(self)

Or it can disable a feature for the document and its descendants:

Permissions-Policy: camera=(), microphone=()

These declarations set policy boundaries. They do not bypass browser prompts, operating-system controls, secure-context requirements, or application authorization. If camera use is allowed by policy, the browser can still require user permission before an API succeeds.

That distinction matters in security reviews. A permissive policy is not equivalent to a successful capability request, while a restrictive policy can stop a request before user permission would otherwise become relevant.

Embedded frames need deliberate delegation

Third-party frames are a common place for capability boundaries to become unclear. A page may embed payment, support, analytics, media, or collaboration interfaces. Most such frames do not need every browser feature available to the top-level application.

A top-level response can name an origin for a feature:

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

An iframe can also carry an allow attribute that further constrains features for that frame:

<iframe
  src="https://calls.example/room"
  allow="microphone"
></iframe>

The effective permission is constrained by the surrounding policy chain. An iframe attribute cannot expand access beyond a restriction imposed by an ancestor response policy. Treat delegation as an explicit contract: the embedding page identifies the feature and the recipient context rather than relying on broad ambient access.

Origin matching also deserves care. A named origin is a security boundary, not a label for every service operated by the same organization. Scheme, host, and port participate in origin identity. Deployment changes therefore need policy review when an embedded service moves between origins.

Defaults vary by feature

There is no safe assumption that every controlled feature has the same default allowlist. Feature definitions can differ, and browser support can vary. A policy should be based on the specific features used by the application rather than copied from an unrelated header set.

This also makes a giant deny list fragile. A long list can look comprehensive while omitting a feature that matters to the application or naming a directive unsupported by a target browser. A smaller policy tied to an inventory of actual capability use is easier to test and maintain.

For high-value boundaries, test behavior in the browsers the application supports. Header presence alone does not prove that a particular feature is governed as intended.

Policy does not replace user permission

Consider geolocation. A site may send:

Permissions-Policy: geolocation=(self)

That restricts eligibility to the same origin. JavaScript on that origin still faces the browser’s geolocation permission model. The header does not silently approve a location request.

The reverse is also important. A user may previously have granted geolocation permission to an origin, yet an embedding policy can still make the feature unavailable in a particular document context. Browser permission and document policy answer different questions.

This separation supports defense in depth. User consent governs whether a capable context may receive sensitive access. Document policy narrows which contexts are capable of asking in the first place.

Policy is not a sandbox

Permissions-Policy is not a general isolation mechanism for hostile content. It does not replace the iframe sandbox attribute, Content Security Policy, origin separation, authentication, or authorization.

A frame denied camera access can still have other powers granted by its origin, credentials, DOM context, network access, or embedding configuration. Security design must account for those channels independently.

Likewise, allowing a feature does not validate the code that uses it. If an embedded origin is compromised, delegated capabilities may become useful to the attacker within the limits imposed by browser policy and user permission. Delegate only what the embedded function requires.

Roll out from a capability inventory

Start with the application paths that invoke controlled browser features. Record the top-level origin, embedded origins, required features, and expected browser support. Then define policy from that inventory.

A restrictive baseline might look like:

Permissions-Policy: camera=(), microphone=(), geolocation=()

Applications that require one of those features can open only the needed boundary. For example, a first-party location page could use geolocation=(self) while keeping camera and microphone disabled.

Test top-level and embedded flows after deployment. Include both positive cases, where an intended feature still works, and negative cases, where an unrelated frame cannot use it. Also inspect the final response after CDN, reverse-proxy, and application middleware processing so the policy being tested is the policy the browser receives.

A useful policy is narrow enough to express architectural intent and small enough to stay accurate. When capability needs change, update the inventory and the header together rather than letting old delegation survive as invisible privilege.