The Permissions-Policy header controls which browser features and APIs — camera, microphone, geolocation, and others — can be used by your page and by any embedded iframes. Setting it explicitly blocks features you do not use, preventing malicious or compromised third-party embeds from silently accessing sensitive device capabilities.
Choosing Your Policy
Build your policy by listing each feature with an allowlist. Start with everything blocked and open up only what your application genuinely needs:
| Allowlist Syntax | Meaning |
|---|---|
feature=() | Block entirely — nobody can use this feature |
feature=(self) | Allow for your own origin only — not embedded iframes |
feature=* | Allow for all origins |
feature=(self "https://widget.example.com") | Allow for your origin plus a specific third-party |
Recommended Starting Policy
Permissions-Policy: camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=(), autoplay=(self), usb=(), bluetooth=(), accelerometer=(), gyroscope=(), magnetometer=(), display-capture=()Adjust fullscreen and autoplay to () if your site does not use those features either.
Nginx
server {
listen 443 ssl;
server_name example.com;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()" always;
# ... rest of config
}sudo nginx -t && sudo systemctl reload nginxApache
<VirtualHost *:443>
<IfModule mod_headers.c>
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()"
</IfModule>
</VirtualHost>Or in .htaccess:
<IfModule mod_headers.c>
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()"
</IfModule>sudo a2enmod headers && sudo systemctl reload apache2IIS (web.config)
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Permissions-Policy"
value="camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()" />
</customHeaders>
</httpProtocol>
</system.webServer>Caddy
example.com {
header Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()"
# ... rest of config
}Cloudflare (Transform Rules)
- Go to Rules → Transform Rules → Modify Response Header.
- Add a rule with Operation: Set, Header name: Permissions-Policy.
- Enter your policy as the value.
- Deploy the rule.
Next.js
// next.config.mjs
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=()',
},
],
},
];
},
};
export default nextConfig;Granting Permissions to Specific iframes
If you need a specific embedded widget to access a feature you have blocked globally, you can grant it via the allow attribute on the individual <iframe> element. The page-level Permissions-Policy sets the ceiling — the iframe allow attribute can only restrict further, not expand beyond it.
<!-- Allow geolocation only for this specific embed -->
<iframe
src="https://maps.example.com/embed"
allow="geolocation 'src'"
></iframe>geolocation=(), adding allow="geolocation" to an iframe has no effect. The header takes precedence. To allow geolocation in an iframe, your page-level policy must permit at least geolocation=(self) or a specific origin.Verifying the Header
curl -I https://example.com | grep -i permissions
# Expected: permissions-policy: camera=(), microphone=(), ...Or use the ShowDNS Security Headers Scanner.