Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
SecurityMedium

Set a Permissions-Policy header

The Permissions-Policy header lets servers restrict which browser features (camera, microphone, geolocation, etc.) can be used in a page or its embedded iframes.

Utilities
Quick take
Typical fix time 15 min
  • Use `Permissions-Policy` to disable browser features your site does not use (camera, microphone, geolocation, payment)
  • Syntax: `Permissions-Policy: camera=(), microphone=(), geolocation=()` — empty `()` means denied to all
  • Previously called `Feature-Policy` — the old syntax is deprecated and only supported in older browsers
  • Restricting unused features limits the blast radius if your site is compromised by XSS
  • Third-party iframes inherit page restrictions unless you explicitly grant them permissions
Why it matters: A site compromised by XSS that has unrestricted camera and microphone access can silently record the user. Permissions-Policy limits which browser APIs are available, reducing attacker capabilities even after a successful injection.

Rule Details

Permissions-Policy (opens in new tab) (formerly Feature-Policy) is an HTTP response header that controls access to browser features and APIs. It applies to the page itself and any iframes it embeds, and the specification (opens in new tab) defines exactly how those inherited restrictions work.

Code Example

Permissions-Policy: <feature>=(<allowlist>), <feature>=(<allowlist>)
Allowlist syntaxMeaning
()Feature denied for all origins
(self)Feature allowed only for same-origin
("https://trusted.com")Feature allowed for this specific origin
(self "https://trusted.com")Feature allowed for same-origin and trusted.com
*Feature allowed for all origins (permissive — avoid)

Why It Matters

A site compromised by XSS that has unrestricted camera and microphone access can silently record the user. Permissions-Policy limits which browser APIs are available, reducing attacker capabilities even after a successful injection.

Commonly Restricted Features

FeatureDescriptionDeny if...
cameragetUserMedia({ video: true })Site does not do video calls
microphonegetUserMedia({ audio: true })Site does not record audio
geolocationnavigator.geolocationSite does not need location
paymentPayment Request APISite does not accept payments in-browser
usbWebUSB APINo hardware interaction needed
fullscreenElement.requestFullscreen()No video player
autoplayMedia autoplayNo autoplay needed
accelerometerDevice orientationNo motion detection
gyroscopeGyroscope sensorNo motion detection
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()

The interest-cohort=() directive opts your site out of Google's deprecated FLoC tracking API, and OWASP's security headers guidance (opens in new tab) is a good cross-check for the rest of your baseline header set.

Server Configuration

Nginx

add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;

Apache

<IfModule mod_headers.c>
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
</IfModule>

Next.js

// next.config.js
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Permissions-Policy',
            value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=()',
          },
        ],
      },
    ]
  },
}

Express.js (using Helmet)

import helmet from 'helmet'
 
app.use(
  helmet.permittedCrossDomainPolicies()
  // Or configure manually:
)
 
// For granular control, set the header directly:
app.use((req, res, next) => {
  res.setHeader(
    'Permissions-Policy',
    'camera=(), microphone=(), geolocation=(), payment=(), usb=()'
  )
  next()
})

Controlling iframe Permissions

When embedding a third-party iframe, use the HTML allow attribute to grant specific permissions:

<!-- Deny all features to this iframe (default with Permissions-Policy: camera=()) -->
<iframe src="https://example.com/widget"></iframe>
 
<!-- Explicitly allow camera for a video call widget -->
<iframe
  src="https://video.example.com"
  allow="camera 'self' https://video.example.com; microphone 'self' https://video.example.com"
></iframe>
 
<!-- Allow fullscreen for a video player -->
<iframe
  src="https://player.example.com/embed/123"
  allow="fullscreen"
  allowfullscreen
></iframe>

Legacy Feature-Policy (Deprecated)

The old Feature-Policy header used a different syntax and is deprecated. Do not use it for new sites:

❌ Deprecated
Feature-Policy: camera 'none'; microphone 'none'; geolocation 'none'
 
✅ Current
Permissions-Policy: camera=(), microphone=(), geolocation=()

Exceptions

  • A missing or weak header should be evaluated against the live production response path, not only the framework or server config in isolation.
  • Legacy integrations or embedded third-party content may require narrowly scoped exceptions, but they should be documented explicitly instead of left permissive by default.
  • When multiple security headers are missing, prioritize the header that removes the highest exploitability or browser capability first.

Standards

  • Align the implementation with MDN: Permissions-Policy and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with W3C: Permissions Policy specification and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with OWASP: Security Headers Cheat Sheet and verify the effective response or browser behavior, not only the configuration file.

Support Notes

  • Current project targets outside the feature support range: and_ff 147, firefox 148, firefox 147, firefox 140, ios_saf 26.3, ios_saf 26.2, ios_saf 18.5-18.7, safari 26.3, safari 26.2.
  • Baseline-compatible minimums: chrome 115, edge 115, firefox 116, safari 16.4, safari_ios 16.4.
  • Add a fallback or a narrower policy note when a required project target falls outside that support range.

Verification

Automated Checks

  • Inspect the effective response headers with curl, a security header scanner, or equivalent tooling against representative live responses.

Manual Checks

  • Verify the browser or user-facing behavior manually in a production-like flow and confirm there is no stronger conflicting security signal.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Check whether the server sends a Permissions-Policy header and review which browser features are allowed or denied. Identify any powerful features (camera, microphone, geolocation, payment, USB) that are enabled but not required by the application.

Fix

Auto-fix issues

Add a Permissions-Policy header that disables all browser features your site does not use. Start with camera=(), microphone=(), geolocation=() and expand the list based on what the application actually needs.

Explain

Learn more

Explain what the Permissions-Policy header does, how it restricts browser APIs like camera and geolocation, and why disabling unused features improves security.

Review

Code review

Review server config, headers, forms, and integration points related to Set a Permissions-Policy header. Flag exact responses, cookies, or browser behaviors that violate the rule, and verify them against the effective production-like response.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Mozilla Observatory
observatory.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Set an X-Frame-Options header

The X-Frame-Options header controls whether your page can be embedded in an iframe, frame, or object — preventing clickjacking attacks.

Security
Implement a content security policy

A Content Security Policy is implemented to prevent XSS attacks and control resource loading.

Security
External Link Security

Links that open in a new tab using target='_blank' must include rel='noopener noreferrer' to prevent the opened page from accessing the opener's window context.

Security

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385