Skip to main content
CodeRocket
Securitymediumheaders

Set a Permissions-Policy header

rule · permissions-policy

Permissions-Policy (opens in a 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 a new tab) defines exactly how those inherited restrictions work.

Code Example

HTTP
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
HTTP
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 a new tab) is a good cross-check for the rest of your baseline header set.

Server Configuration

Nginx

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

Apache

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

Next.js

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

Express.js (using Helmet)

JavaScript
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:

HTML
<!-- 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:

HTTP
❌ 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.