Skip to content
Issue docs

No Permissions-Policy header

Standardpermissions_policyIssue 125

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-Policy header.

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

  1. 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=()
  2. Allow only what a page actually needs, and only to whom. self permits your own origin; a specific origin can be named for a trusted embed:

    Permissions-Policy: geolocation=(self), camera=("https://meet.example.com")
  3. 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.

  4. 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 add Permissions-Policy alongside.

  5. Do not use a meta tag. Browsers honour this only as a response header.

  6. 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-8

Corrected 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

  1. Reads the response headers for the page as it was served.

  2. Flattens a repeated header, joining a comma-separated or list-shaped value, so a server that sends the header twice is not reported.

  3. Reports when the header is absent, empty, or whitespace only.

  4. Does not accept Feature-Policy as a substitute. It is deprecated and no current browser acts on it, so counting it would mark an unprotected page as protected.

  5. Does not accept a meta tag, for the same reason: browsers honour this only as a real response header.

  6. 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.

Further reading