How do I fix a missing Permissions-Policy header?
A missing Permissions-Policy header means your responses carry no instruction telling the browser which powerful features a document and its embedded frames may use. The Permissions-Policy header provides a mechanism to allow and deny the use of browser features in a document or within any iframe elements in the document. Because the policy is delivered as a response header, the fix is to emit it from the CDN, reverse proxy, or application server that serves your HTML.
Why this matters
When the Permissions-Policy header is absent, every directive falls back to its default allowlist, which for many features is the wildcard or the same origin. That silent default is why scanners flag a missing Permissions-Policy header even though the site still renders: nothing tells the browser to refuse the camera, microphone, geolocation, or payment APIs, and a compromised or third-party frame can still request them. The header also complements Content-Security-Policy. CSP restricts where code may load from, while Permissions Policy restricts which device capabilities that code may reach. Setting a directive to an empty allowlist disables the feature in the top-level document and in all nested browsing contexts, regardless of origin, and the equivalent value for an iframe allow attribute is the literal name for none. MDN flags the header as an experimental technology with limited availability, so check the browser compatibility table before you treat it as a hard security boundary.
How to fix it
Send the header from whatever terminates TLS for your site. The value is a comma-separated list of directives, and each directive is a feature name, an equals sign, and a parenthesized allowlist. Use an empty pair of parentheses to disable a feature everywhere, or list a quoted origin inside the parentheses to allow exactly that origin. In Nginx the add_header directive takes a name and a value and accepts an always parameter; Apache's mod_headers module provides Header always set; Express exposes res.set(field, value); Next.js applies headers from next.config.js; Cloudflare Pages reads a _headers file; and a Cloudflare Worker can attach headers through a Response init object. Start by disabling the features your application does not use, such as geolocation, microphone, and camera.
# Nginx
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Apache
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
// Express
app.use(function (req, res, next) {
res.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
next();
});
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'Permissions-Policy', value: 'geolocation=(), microphone=(), camera=()' },
],
},
]
},
}
# Cloudflare Pages public/_headers
/*
Permissions-Policy: geolocation=(), microphone=(), camera=()
How to verify the fix
Request a real HTML page and confirm the header appears on the document response. The one-liner below sends a HEAD request and filters the output, and the same value is visible in browser DevTools under Network, on the document request, in the Response Headers pane. Check more than one route, because a CDN rule scoped to a path prefix will not protect your marketing pages.
curl -sI https://example.com/ | grep -i permissions-policy
Automated check
The Auditor fetches your page, inspects the response headers, and raises a Missing Permissions-Policy header finding at Low severity when the header is absent. That tells you the control is missing; it does not judge whether the allowlists match your application, so review each directive against MDN's Permissions-Policy reference and disable anything you do not use.
Scan your app free →