What Permissions Policy controls — and what it does not
Browsers offer web pages capabilities beyond displaying content: the camera and microphone, location, fullscreen, screen sharing, USB devices or the Payment Request API. Many are policy-controlled features. Each feature's specification defines a default allowlist. A Permissions Policy can restrict a feature for a document and selectively delegate it to embedded frames. W3C Permissions Policy: policy-controlled features
Two delivery mechanisms work together:
- The HTTP response header
Permissions-Policyapplies to the document it accompanies. It also limits what that document can delegate to embedded frames. - The
allowattribute on aniframespecifies which features the embedded content should receive.
The specification does not provide a variant using an HTML meta element. W3C: header and container policies, W3C: delivery
No header does not mean ‘everything allowed’. The camera, microphone, location, payment interface, screen capture, fullscreen and autoplay have the default allowlist 'self'. They are available to the top-level document and frames of the same origin; a cross-origin frame receives them only if the embedding document delegates them through allow. An absent header is therefore not evidence of an exploitable vulnerability. W3C: default allowlists, Media Capture and Streams: camera and microphone, Geolocation API
On narrow screens, scroll the table sideways.
| Mechanism | Question it answers | Limitation |
|---|---|---|
Permissions Policy (Permissions-Policy, allow) | Is a feature available at all in this document or frame? | Does not grant user permission. Does not distinguish your own scripts from injected scripts in the same document. |
| User permission and the Permissions API | Has the person consented to use, and what is the current permission state? | A feature blocked by Permissions Policy appears as denied. The policy can never grant a permission. |
| Content Security Policy | Which scripts, resources and embeddings may the document load or execute? | Does not control device or browser interfaces such as the camera or location. |
The iframe sandbox attribute | Which basic capabilities, such as scripts, forms or popups, does a frame receive? | A separate model; Permissions Policy supplements rather than duplicates it. |
The Permissions specification explicitly describes the distinction from user permission: a feature can never become ‘granted’ through Permissions Policy, only through the person's consent or a browser rule. W3C Permissions: relationship to Permissions Policy, W3C: other and related mechanisms
This explains the security effect: a feature you block for the entire document, for example with camera=(), is also unavailable to a compromised or injected script in that document. Features your page uses itself remain available to any script running in your document's context. self means the document's origin, not ‘your own scripts’. Other measures address injected code, including safe output, Content Security Policy and Subresource Integrity.
The Web Security Check reads the Permissions-Policy header of the final retrieved response, checks its syntax and assesses whether selected features such as the camera, microphone and location are blocked or limited to the same origin. This snapshot does not cover allow attributes, Permissions-Policy-Report-Only, other pages or a browser's actual behaviour.
How the header and allow work together
Each document first receives an inherited policy. For the top-level document, it follows from the default allowlists. In a frame, it follows from the embedding document's policy and the iframe's allow attribute. A document's own header can then only restrict features further. A frame cannot use its own header to reopen something the embedding document has blocked. W3C: inherited policies, W3C: create a policy from a response
For a cross-origin frame and features with the default allowlist 'self', the embedding document's header must not exclude the frame's origin, and the iframe must delegate the feature through allow. The embedding document itself must also be eligible to receive the feature; a list containing only the other origin is insufficient. The frame's own header or a restriction higher up the frame chain can also deny the feature. These conditions concern policy; secure contexts, user permissions and other API prerequisites still apply. W3C: inheritance in a container
Local test · Chromium 147 · geolocation example
When an embedded frame receives a feature
Embedding document at http://127.0.0.1:PORT, frame at http://localhost:PORT. These are different origins because the host differs. PORT represents the randomly chosen local port.
On narrow screens, scroll the table sideways.
| Embedding document's header | allow on the iframe | Geolocation in the frame |
|---|---|---|
| No header | Absent | Blocked: default allowlist 'self' |
| No header | allow="geolocation" | Available |
geolocation=(self) | allow="geolocation" | Blocked: the header excludes the frame's origin. |
geolocation=(self "http://localhost:PORT") | Absent | Blocked: delegation through allow is missing. |
geolocation=(self "http://localhost:PORT") | allow="geolocation" | Available |
geolocation=* | Absent | Blocked: even * does not replace allow. |
geolocation=() | allow="geolocation" | Blocked |
‘Available’ means only that the policy permits the feature. The actual location request still failed in the test because user permission had not been granted.
Browser support differs substantially. According to MDN compatibility data retrieved on 27 September 2026, Chrome has processed the Permissions-Policy header since version 85; Chromium-based browsers such as Edge follow this implementation. Firefox and Safari do not support the header. Chrome has recognised the allow attribute since version 60, Firefox since 74 and Safari since 11.1, each with its own list of supported features. For Safari, the data lists, for example, camera, microphone and display-capture in the attribute, but not geolocation or payment. MDN compatibility data: Permissions-Policy, MDN compatibility data: iframe allow
Which features you can control
The following selection covers common questions. The working group maintains a non-normative list of standardised, proposed and experimental features; no browser is required to support every one. W3C: feature list for the specification
On narrow screens, scroll the table sideways.
| Feature | Controls, for example | Default allowlist | What blocking it may disrupt |
|---|---|---|---|
camera | Camera access through getUserMedia() | 'self' | Video consultations, identity checks, document scanning in the browser |
microphone | Microphone access | 'self' | Video conferences, voice recording, dictation |
geolocation | Location requests | 'self' | Branch finders, map widgets, delivery areas |
payment | Payment Request API | 'self' | Browser-based payment methods, including in embedded payment services |
usb | WebUSB | 'self' | Device configuration through the browser |
display-capture | Screen sharing through getDisplayMedia() | 'self' | Screen sharing in conferencing and support tools |
fullscreen | Fullscreen through requestFullscreen() | 'self' | Fullscreen mode in embedded video players |
autoplay | Automatic media playback | 'self' | Embedded videos that start automatically |
Sources for the default allowlists: Media Capture and Streams, Geolocation, Payment Request, Screen Capture, Fullscreen, HTML: autoplay, WebUSB: Permissions Policy
The table is not a template. Whether () or permission is appropriate depends solely on whether your document or an embedding actually needs the feature. For example, restricting autoplay with autoplay=(self) prevents video players embedded from other origins from receiving it even with allow="autoplay".
Write the header correctly
The value of Permissions-Policy is a Structured Field Dictionary under RFC 9651. Features are separated by commas, and each receives an allowlist. Feature names are lowercase; origins are strings in double quotes. W3C: structured header serialisation, RFC 9651: dictionaries
On narrow screens, scroll the table sideways.
| Header syntax | Meaning |
|---|---|
camera=() | An empty allowlist: the feature is blocked in the document and every frame. |
geolocation=(self) or geolocation=self | Only the document's origin; frames of the same origin inherit the feature. |
geolocation=(self "https://maps.example.com") | Additionally this origin. A frame of that origin still needs allow. |
payment=(self "https://*.example.com") | Subdomains through a wildcard. This does not include https://example.com itself. |
fullscreen=* | All origins. Frames with the default allowlist 'self' still need allow. |
According to MDN compatibility data, subdomain wildcards have been supported only since Chrome 108. Different subdomains and ports are different origins; https://www.example.com does not cover https://shop.example.com. W3C: examples
The following model suits a website that neither uses any of these features itself nor needs to delegate them to an embedding. It is a header example, not a tested template for your application:
Permissions-Policy: camera=(), microphone=(), geolocation=(), display-capture=(), usb=()Further observations from that test, consistent with the rules:
- The browser ignores unknown feature names individually; the remaining entries continue to apply.
- Chromium rejected unquoted origins, such as
(self http://localhost:PORT), as an invalid entry with a console warning. The frame did not receive the feature. noneis not a header keyword. Chromium reportedgeolocation=noneas invalid and treated the allowlist as empty. Use()to block a feature.- Multiple header lines are combined into a list. If two lines name the same feature, the last value applies. If both the application and CDN set the header, the order in the delivered response therefore decides the result. RFC 9651: parsing dictionaries
The allow attribute uses different syntax: semicolons separate features, keywords use single quotes, and origins are unquoted. Without an allowlist, 'src' applies, meaning the origin of the URL in the src attribute. W3C: HTML attribute serialisation, W3C: the allow attribute
<iframe src="https://maps.example.com/embed" allow="geolocation"></iframe>
<iframe src="https://video.example.net/room" allow="camera; microphone; display-capture"></iframe>Introduce Permissions Policy in four steps
1. Inventory features and embeddings in use
Identify which features your own pages use and which features each embedded service needs: maps, video players, video conferencing, payment services, chat or support tools, login and identity services, and extensions in content management and shop systems. Record each embedding's exact origin, including subdomain and port.
Also decide which responsible output layer sets the header: application, web server, reverse proxy or CDN. Two layers independently adding values can easily produce conflicting entries. The article on implementation and architecture covers these responsibilities. Permissions Policy applies per document: if the same restriction should apply to every page, it must also accompany login, checkout and error documents.
2. Observe new restrictions first
In Chromium-based browsers, you can initially observe rules through Permissions-Policy-Report-Only. According to MDN compatibility data, this header has been available since Chrome 120; Firefox and Safari do not support it. In the documented local test, geolocation remained allowed under Permissions-Policy-Report-Only: geolocation=(); the browser generated a report labelled report. W3C: Report-Only header, MDN compatibility data: Report-Only
Reporting-Endpoints: pp-endpoint="https://reports.example.com/permissions-policy"
Permissions-Policy-Report-Only: camera=();report-to=pp-endpoint, microphone=();report-to=pp-endpoint, geolocation=();report-to=pp-endpointhttps://reports.example.com/permissions-policy is a placeholder for a receiver you operate; the header does not set one up. Reports may arise when a feature is attempted; the specification also provides reports about potential conflicts between frame delegation and inherited policy. A Report-Only policy does not override restrictions already enforced in parallel. An absence of reports therefore does not prove that no page or embedding needs the feature. The Reporting API guide explains delivery and assessment of reports. W3C: reporting
3. Block unnecessary features and selectively permit those needed
The following snippets show the configuration form for nginx and Apache. They have not been executed and assume that your pages do not need the listed features.
nginx: add_header operates in the http, server or location context. These directives are inherited only when the current level has no add_header directive of its own; since nginx 1.29.3, add_header_inherit can change this. Without always, nginx adds the header only for certain status codes, such as 200 or 301. A successful reload is required after the change. nginx: add_header
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;Apache: the snippet requires mod_headers; configuration in .htaccess must be permitted, for example through AllowOverride FileInfo. If the module is not loaded, Apache silently skips the contents of IfModule. always and the default onsuccess table are separate header lists; a header supplied by a backend may consequently arrive twice. Apache: Header directive
<IfModule mod_headers.c>
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
</IfModule>payment=() is appropriate only if neither your page nor an embedded payment service uses the Payment Request API. If an embedding needs a feature, permit it in both places. In the following model, only the video service is delegated camera and microphone access:
Permissions-Policy: camera=(self "https://video.example.net"), microphone=(self "https://video.example.net"), geolocation=()<iframe src="https://video.example.net/room/123" allow="camera; microphone"></iframe>https://video.example.net represents the embedded service's actual origin. If it redirects the frame to a different origin, permission does not automatically apply there.
4. Verify the effect and keep a rollback path
First check which header actually arrives. For an address you own and are authorised to check, the following command shows the headers of each response in a redirect chain; the final block belongs to the final response:
curl -sS -L -D - -o /dev/null https://www.example.com/https://www.example.com/ is a placeholder; the command was not run against a real website. It shows an individual response, not browser behaviour.
In the browser, Chromium reports violations in the console, such as ‘Permissions policy violation: Geolocation access has been blocked …’, and identifies header parsing errors. Chromium exposes the effective-policy query under the older name document.featurePolicy, for example document.featurePolicy.allowsFeature('camera'). The specification calls the interface document.permissionsPolicy; in the documented Chromium 141 test, that name was absent. Also test required embeddings in Firefox and Safari, where only the allow attribute and the browsers' own defaults apply. W3C: policy introspection
Rollback is simpler than for HSTS: browsers do not store a Permissions Policy beyond the document. Removing or relaxing the header at the responsible output layer affects newly loaded documents. Cached HTML responses in a CDN, browser cache or service worker may still deliver the old header until refreshed, however. Plan to clear the relevant caches where necessary. The Cache-Control guide explains these caches.
Interpret findings correctly
An absent header shows only that the particular browser's default rules apply to this document. A present header shows which rules a supporting browser can apply to this response. Neither observation proves or disproves an exploitable vulnerability.
On narrow screens, scroll the table sideways.
| Observation | What it shows | Next step |
|---|---|---|
| Header absent from the checked response | No explicit header policy is present; browser defaults and inherited frame restrictions may still apply. | Review the inventory; block unnecessary features where appropriate. Other pages may differ. |
| Header present, features blocked | Supporting browsers block these features for the document and its frames. | No protection from injected code using permitted features; no effect in browsers without header support. |
| Console reports a Structured Field parsing error | The browser discards the entire field. Distinguish this from warnings about individual allowlist entries. | Correct the syntax: double quotes, lowercase names, commas. |
| Two header lines, or a CDN adds values | The lines are combined; the last value for each feature applies. | Assign one output layer and inspect the delivered response. |
Feature blocked in the frame despite allow | Possible cause: the header excludes the frame's or parent's origin. Other ancestors, the frame's header, browser support or user permissions may also be involved. | Check the entire frame chain and the specific API error; extend only an intended permission. |
| Feature blocked in the frame despite permission in the header | Possible cause: the allow attribute is absent or does not name the feature. | Add allow to the relevant iframe. |
Feature-Policy and Permissions-Policy in parallel | For features named in both, Chromium uses the value from Permissions-Policy. | Remove the old header after migration. |
A Web Security Check finding concerns the header of one retrieved response at the time of retrieval. Whether embeddings work and which features are actually available in the browser can only be established through the browser checks in step 4.
Migrate from Feature-Policy
Permissions Policy was previously called Feature Policy. The old Feature-Policy header has different syntax and is deprecated. According to MDN compatibility data, Chrome has continued processing it since version 60; Firefox and Safari do not process it. W3C: introduction, MDN compatibility data: Feature-Policy
On narrow screens, scroll the table sideways.
Feature-Policy (deprecated) | Permissions-Policy |
|---|---|
camera 'none' | camera=() |
geolocation 'self' | geolocation=(self) |
geolocation 'self' https://maps.example.com | geolocation=(self "https://maps.example.com") |
fullscreen * | fullscreen=* |
Entries separated by ; | Entries separated by , |
In the documented local Chromium 147 test, Feature-Policy: geolocation 'none' on its own still blocked geolocation. When both headers were present, the value from Permissions-Policy applied for each feature; the console reported the duplication. A feature listed only in the old header remained blocked. Chrome describes Feature-Policy as a lower-priority alias. Chrome for Developers: Permissions Policy
Account for a behavioural change during migration: under Feature Policy, an origin in the header list could be enough to give a cross-origin frame a feature. Under Permissions Policy, the frame also needs allow. Translate the old header, test affected embeddings, then remove Feature-Policy. The advanced headers guide discusses other deprecated headers.
Evidence and context
Permissions Policy is a technical measure restricting selected browser features. A present header is neither a universal legal requirement nor proof of compliance. When documenting the measure, record the tested URL, time, delivered header, affected embeddings and tested browsers.
Data protection principles such as data minimisation and data protection by default (Articles 5 and 25 GDPR, BfDI collection of legislation) concern the entire processing activity. Blocking a device interface can contribute, but does not replace assessment of the processing. For entities within the scope of Germany's BSIG, § 30 requires appropriate risk management measures; which headers form part of those measures follows from the organisation's risk assessment, not from the legislation. Permissions Policy is also neither a script inventory nor an inspection of included scripts' contents. The compliance overview and NIS2 checklist put these connections in context.
Related topics
To first see which header a publicly accessible page delivers, open the Web Security Check. It assesses an individual response; it does not replace the embedding and browser tests described above.
Frequently asked questions
What is Permissions Policy?
Permissions Policy is a browser mechanism through which a website defines whether policy-controlled features such as the camera, microphone or location are available in its document and embedded frames. It is controlled through the Permissions-Policy header and the iframe allow attribute. In supporting browsers, camera=() blocks the camera for the document and all frames. User permission does not override the policy.
Which browsers support the header?
According to MDN compatibility data, Chrome has processed the header since version 85, as have Chromium-based browsers such as Edge. Firefox and Safari do not support the header, but recognise the allow attribute for individual features. Sending a header therefore does not mean that every visitor receives the same restrictions.
Can Permissions Policy disrupt my website?
Yes. A restriction can prevent video calls, map features, screen sharing, fullscreen or browser-based payment methods, including within embedded services. Identify required features first, observe new rules in Chromium with Permissions-Policy-Report-Only, and test embeddings in every target browser.
How does it differ from Feature-Policy?
Feature-Policy is the deprecated predecessor with different syntax, such as camera 'none' instead of camera=(). Permissions-Policy uses a Structured Field Dictionary with commas between features, unquoted self and origins in double quotes. Under Permissions Policy, a cross-origin frame also needs the allow attribute for features such as the camera or location.
Do I need Permissions Policy if I do not use the camera?
Explicitly blocking unnecessary features can be useful because it also makes those features unavailable to embedded or injected content within the document. An absent header does not prove unnoticed camera access, however: browser defaults, secure contexts and user permissions still apply.
Does Permissions Policy protect against compromised third-party scripts?
Only to a limited extent. A script running in your document has the same features as your page. It cannot use blocked features, but can use permitted ones. Other measures, such as Content Security Policy, Subresource Integrity and oversight of included services, help address manipulated or injected scripts.
Why does a feature not work in an iframe despite allow?
Often, the embedding page's Permissions-Policy header excludes the frame's origin, for example with geolocation=(self). For a cross-origin frame, both the header and allow must permit the feature. Other causes include an invalid header, a frame redirect to another origin, or a browser that does not support that feature in the attribute.
Is Permissions Policy relevant to NIS2?
It can be a technical measure to restrict certain browser features. The header alone establishes neither NIS2 compliance nor complete data minimisation. The measures required depend on the application's needs, risk assessment and actual effects in the browsers used.
Sources and technical reference date
- W3C Permissions Policy
: Working Draft of 22 September 2026; the Editor’s Draft had the same reference date. Mechanism, syntax, inheritance, Report-Only and reporting. Not yet a W3C Recommendation. - Feature list for the specification
: non-normative overview of standardised, proposed, experimental and withdrawn features. - W3C Permissions, Annex A
: Editor’s Draft of 6 October 2025 in the source review; relationship between user permission and Permissions Policy.
- Default allowlists from Media Capture and Streams (Candidate Recommendation Draft, 9 October 2025), Geolocation (Candidate Recommendation Snapshot, 26 March 2026), Payment Request (Candidate Recommendation Draft, 22 June 2026), Screen Capture (Working Draft, 27 August 2026), HTML and Fullscreen (Living Standards).
- RFC 9651: Structured Field Values for HTTP, September 2024; replaces RFC 8941. Parsing, dictionaries and duplicate keys.
- MDN: Permissions-Policy and MDN compatibility data: browser explanation and support for the header, Report-Only, Feature-Policy and the
allowattribute, retrieved on 27 September 2026. - Chrome for Developers: Permissions Policy: relationship to Feature-Policy in Chrome; page reference date April 2022.
- nginx: headers module and Apache: mod_headers: prerequisites for the configuration snippets.
Source reference date: 27 September 2026. The interaction between the header and allow, syntax errors, duplicate headers, Feature-Policy and Report-Only were tested locally in headless Chromium 147.0.7727.15 on a test server during that review. An older Chromium 141 test establishes the introspection name stated there. Firefox, Safari, the current stable Chrome release and the nginx/Apache examples were not practically tested.