Permissions-Policy: Locking Down Browser Features

Modern browsers expose powerful capabilities to web pages — the camera, the microphone, precise location, motion sensors and more. The Permissions-Policy header lets you declare which of those features your site uses, and switch off everything else, so injected or embedded code cannot quietly reach for them.

The short version

Send a Permissions-Policy header that disables the features you do not use, e.g. geolocation=(), camera=(), microphone=(). An empty allowlist () blocks a feature entirely; (self) allows only your own origin. It replaces the older, incompatible Feature-Policy header. Check what your site sends with our headers check.

Why restrict browser features

By default, the powerful features a browser offers are available to your top-level page and, in many cases, to content embedded in iframes. If your site never needs the camera, there is no reason to leave that door open. Locking features down shrinks your attack surface in two ways: injected scripts cannot silently request sensitive capabilities, and third-party iframes — ads, widgets, embeds — cannot use them under cover of your origin. It is also a clear statement of intent that helps you reason about what your pages are permitted to do.

The syntax

The header is a comma-separated list of feature = allowlist pairs. The allowlist is written in parentheses.

Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()
AllowlistMeaning
()Empty list — the feature is disabled for everyone, including your own page.
(self)Allowed only for your own origin.
(self "https://maps.example.com")Allowed for your origin plus the listed origins.
*Allowed for all origins, including embedded iframes. Use sparingly.

So geolocation=(self) lets your own pages ask for location but blocks any embedded third party from doing so, while geolocation=() turns it off completely. Only list the features you actually rely on with (self); disable the rest with ().

A practical starting policy

Permissions-Policy: accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), geolocation=(), gyroscope=(), microphone=(), payment=(), usb=()

This disables the sensitive features most sites never use and allows only fullscreen for your own origin. Adjust it to your needs: a video-conferencing app would set camera=(self) and microphone=(self); a store using a browser payment flow would keep payment=(self). Feature names not present in the header fall back to each browser’s default, so list the ones you care about explicitly.

Work out your own list by auditing what your pages genuinely do. A brochure site or blog usually needs none of these capabilities, so an aggressive policy that disables them all is both safe and a meaningful reduction in attack surface. An application that uses one or two — say location for a store finder — should allow exactly those with (self) and disable the remainder. When in doubt, start restrictive: it is far easier to open a feature back up after you notice it is needed than to discover months later that a third-party embed has been quietly using one.

It replaces Feature-Policy

Permissions-Policy is the successor to the experimental Feature-Policy header, which used a different, incompatible syntax (space-separated origins and no parentheses). Feature-Policy is deprecated. If you still send it, migrate to Permissions-Policy; you can send both during a transition, but new work should target Permissions-Policy only.

Test the features you do use. An over-broad policy can silently break real functionality — a map that cannot read location, a call widget that cannot open the microphone, an embedded video that will not go fullscreen. After deploying, walk through every feature your site legitimately needs and confirm it still works, rather than assuming an empty policy is always safe.

Where it fits

Setting the header is straightforward. On Apache use Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"; on Nginx use add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;. It rounds out the security headers checklist alongside CSP and frame protection. Once configured, run a scan to confirm the header is being sent.

Frequently asked questions

What is the difference between () and (self)?

() disables a feature for everyone, including your own page. (self) keeps it available to your own origin but blocks embedded third parties. Use () for features you never need and (self) for the ones only your own code should use.

Do I still need Feature-Policy?

No. Feature-Policy is deprecated in favour of Permissions-Policy, which uses a cleaner syntax. Migrate any existing Feature-Policy rules and drop the old header once done.

Will this block features on sites I embed my page into?

The policy governs what your page and the iframes it contains may do. To let a third party embedding your content use a feature, you delegate it explicitly through the allowlist; otherwise it stays restricted.

Related guides

Check your site against this guide

Run a free ScanOpsPro scan and see how your site handles the fundamentals.

Run a free scan