Learn how the Permissions-Policy HTTP header works, which browser features to restrict, and how to configure it on Nginx, Apache, Cloudflare, and Next.js.
Anay Pandya
1,149 words
Anay Pandya
Founder of ZeriFlow · 10 years fullstack engineering · About the author
Key Takeaways
The Permissions-Policy HTTP header gives you control over which browser features and APIs your website can use. Think of it as a whitelist for browser capabilities — if you don't need the camera, microphone, or geolocation APIs, you can explicitly disable them.
<div class="zf-stat-callout" style="background:#0d1117;border:1px solid rgba(16,185,129,0.25);border-left:3px solid #10b981;border-radius:4px;padding:16px 20px;margin:24px 0"> <p style="margin:0 0 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.15em;color:#10b981;font-family:monospace">ZeriFlow Data — 12,400+ sites analyzed</p> <p style="margin:0;font-size:13px;color:#e2e8f0;line-height:1.6;font-family:monospace">Only 23% of sites in ZeriFlow's scan corpus implement a Permissions-Policy header to restrict browser API access. Without it, any injected third-party script can silently request camera, microphone, or location access.</p> </div>
Is your site actually secure?
Keep reading
Run a free check — 60 seconds
When set correctly, Permissions-Policy prevents:
Here's a basic example:
Permissions-Policy: camera=(), microphone=(), geolocation=()This tells the browser: "This page and all embedded content are not allowed to use the camera, microphone, or geolocation." The empty parentheses () mean "no origins are allowed."
The Permissions-Policy header is the successor to the older Feature-Policy header. The change happened for several reasons:
Feature-Policy: camera 'none'), while Permissions-Policy uses structured headers (Permissions-Policy: camera=())Important: Some older browsers still only support Feature-Policy. For maximum compatibility, you can set both headers, but Permissions-Policy is the standard going forward.
# Nginx — both headers for compatibility
add_header Feature-Policy "camera 'none'; microphone 'none'; geolocation 'none'" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;The syntax follows the structured headers specification:
Permissions-Policy: <feature>=<allowlist>| Value | Meaning |
|---|---|
() | Disabled for all origins |
(self) | Allowed for same origin only |
* | Allowed for all origins |
("https://example.com") | Allowed for specific origin |
(self "https://example.com") | Allowed for self and specific origin |
Separate features with commas:
Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=(self "https://stripe.com")This policy: - Blocks camera and microphone entirely - Allows geolocation for same-origin only - Allows the Payment API for same-origin and Stripe's domain
Here's a comprehensive list of features you can control, with recommended defaults for most websites:
### Should Almost Always Be Disabled These features are rarely needed and should be blocked by default:
camera=() # Webcam access
microphone=() # Microphone access
geolocation=() # Location access
midi=() # MIDI device access
usb=() # USB device access
bluetooth=() # Bluetooth access
serial=() # Serial port access
hid=() # Human Interface Devices
magnetometer=() # Magnetometer sensor
gyroscope=() # Gyroscope sensor
accelerometer=() # Accelerometer sensor### Context-Dependent Enable these based on your application's needs:
payment=(self) # Payment Request API — enable for e-commerce
fullscreen=(self) # Fullscreen API — enable for media players
picture-in-picture=(self) # PiP — enable for video content
autoplay=(self) # Media autoplay — enable for video sites
display-capture=() # Screen capture — usually disable### Advertising-Related Control features commonly used by ad tech:
attribution-reporting=() # Conversion tracking
browsing-topics=() # Topics API (Privacy Sandbox)
interest-cohort=() # FLoC (deprecated but still worth blocking)server {
listen 443 ssl http2;
server_name example.com;
# Permissions-Policy — block unused browser features
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), midi=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), payment=(), autoplay=(self), fullscreen=(self)" always;
# ... rest of your config
}# In httpd.conf, .htaccess, or VirtualHost block
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), midi=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), payment=(), autoplay=(self), fullscreen=(self)"Make sure mod_headers is enabled:
a2enmod headers
systemctl restart apache2Using Cloudflare Transform Rules:
Permissions-Policycamera=(), microphone=(), geolocation=(), midi=(), usb=()In next.config.js:
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), midi=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()',
},
],
},
];
},
};
module.exports = nextConfig;In vercel.json:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Permissions-Policy",
"value": "camera=(), microphone=(), geolocation=(), midi=(), usb=()"
}
]
}
]
}### Browser DevTools
1. Open DevTools (F12)
2. Go to the Network tab
3. Click on the main document request
4. Look at the Response Headers section for Permissions-Policy
### JavaScript API You can query your permissions programmatically:
// Check if a feature is allowed
navigator.permissions.query({ name: 'camera' }).then(result => {
console.log('Camera permission:', result.state);
// "denied" if Permissions-Policy blocks it
});### Online Tools Run a scan on ZeriFlow to check your Permissions-Policy header alongside all other check your security headers in one test.
curl -s -D - -o /dev/null https://example.com | grep -i permissions-policyPermissions-Policy: camera=(), microphone=(), geolocation=(), midi=(), usb=(), payment=(), autoplay=(), fullscreen=(self)Permissions-Policy: camera=(), microphone=(), geolocation=(self), midi=(), usb=(), payment=(self "https://js.stripe.com"), autoplay=(), fullscreen=(self)Permissions-Policy: camera=(self), microphone=(self), geolocation=(), midi=(), usb=(), payment=(self), autoplay=(self), fullscreen=(self), picture-in-picture=(self)Permissions-Policy: camera=(), microphone=(), geolocation=(), midi=(), usb=(), payment=(self), autoplay=(), fullscreen=(self), clipboard-write=(self)Configuring Permissions-Policy provides several concrete security benefits:
Combined with Content-Security-Policy and other security headers, Permissions-Policy is part of a comprehensive browser security strategy. Check your full header configuration with a ZeriFlow scan to see how all your headers work together.
<!-- zf-internal-links -->
Check all your security headers instantly — free.
See exactly which headers are missing and how to fix them.
Related resources
Website Vulnerability Scanner
Run a broader website security audit across headers, TLS, DNS, cookies, SEO, and disclosure checks.
Security Headers Checker
Check CSP, HSTS, X-Frame-Options, and other response headers.
SSL Checker
Review TLS certificate, HTTPS, and transport security signals.
DMARC Checker
Validate email authentication records for domain spoofing protection.
CSP Checker
Review Content-Security-Policy coverage and common gaps.
September 19, 2025
Referrer-Policy Header: What It Does and How to Set It Correctly
Monitor your security score
Schedule recurring scans and catch regressions after deployments.