No Permissions-Policy header
What is this issue?
This check reports a response that does not carry a Permissions-Policy header.
Permissions-Policy declares which powerful browser features this page -- and
anything it embeds in an iframe -- is allowed to use: camera, microphone,
geolocation, payment, sensors, and others. Without the header, every one of them
stays available by default.
The deprecated Feature-Policy header is not accepted in its place. No
current browser honours it, so a site that ships only Feature-Policy is
genuinely unprotected, and treating it as a pass would report the opposite of
the truth. A <meta http-equiv="Permissions-Policy"> tag is not accepted either,
because browsers do not honour that form.
For a page to pass:
- The response carries a non-empty
Permissions-Policyheader.
A policy that disables everything -- camera=(), microphone=(), geolocation=()
-- is a perfectly good policy and passes.
Why it matters
The header is the only way to say what a page and its embedded content are NOT allowed to do.
- Third-party frames inherit your permissions. An advertisement, a chat widget or an embedded video is running inside your origin's permission envelope. Without a policy, a compromised or careless embed can prompt your visitors for their camera or location, and the prompt names your domain.
- Defence in depth for your own code. A script injected through some other flaw cannot reach a feature the policy has switched off.
- Reduced attack surface for free. Most sites need none of these features on most pages.
This is a standard finding. Nothing about the page stops being crawled,
indexed or served without it, there is no ranking effect, and the fix is one
line of server configuration -- which is the same reasoning that grades
Referrer-Policy at this level.
How to fix it
Start by switching off what you do not use. A good default for most sites disables the features nothing on the page needs:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Allow only what a page actually needs, and only to whom.
selfpermits your own origin; a specific origin can be named for a trusted embed:Permissions-Policy: geolocation=(self), camera=("https://meet.example.com")Set it at the edge. Configure it in the web server or the CDN so it applies to every response, rather than page by page.
Do not rely on
Feature-Policy. It is deprecated and no current browser acts on it. If you have it, keep it if you like, but addPermissions-Policyalongside.Do not use a meta tag. Browsers honour this only as a response header.
Test the pages that DO need a feature. After adding a restrictive policy, check any page with a map, a video call, a camera upload or a payment sheet still works.
Examples
Example 1: A restrictive default
Scenario: A content site that uses none of the powerful features.
Passes because: a non-empty policy is present, and this one switches everything off.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Example 2: No header at all
Scenario: A default server configuration.
Fails because: without the header, every feature stays available to the page and to every third party it embeds.
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8Corrected version: add a policy at the server or CDN.
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Permissions-Policy: camera=(), microphone=(), geolocation=()Example 3: The deprecated header only
Scenario: A policy written when Feature-Policy was the spec.
Fails because: no current browser honours Feature-Policy, so this page is
unprotected in practice.
Feature-Policy: camera 'none'; microphone 'none'Corrected version: add the current header. Keeping the old one alongside does no harm.
Permissions-Policy: camera=(), microphone=()How PixyScan detects this
Reads the response headers for the page as it was served.
Flattens a repeated header, joining a comma-separated or list-shaped value, so a server that sends the header twice is not reported.
Reports when the header is absent, empty, or whitespace only.
Does not accept
Feature-Policyas a substitute. It is deprecated and no current browser acts on it, so counting it would mark an unprotected page as protected.Does not accept a meta tag, for the same reason: browsers honour this only as a real response header.
Runs on HTTP pages too. The policy applies to any document, whatever scheme it arrived over.
The check makes no judgement about WHICH features a policy names. Any non-empty policy is a deliberate statement and passes.
What we store
Storage Level
Page Level
Database Table / Prisma Model
audit_issues.details
Stored Fields
| Field | Type | Description |
|---|---|---|
| message | String | What is missing and what is required instead |
Detection Dependencies
- HTTP Response
Note
Kept on the finding. There is no value to record: the check reports the header being absent or empty, and a page that passes has nothing this check needs to store.