Skip to content
AZ Tools

Permissions-Policy Header Builder

Build the Permissions-Policy header: set camera, microphone, geolocation, payment, fullscreen and 20+ other browser features by origin allowlist.

Presets
Sensors
  • accelerometer
    Read accelerometer
  • gyroscope
    Read gyroscope
  • magnetometer
    Read magnetometer
  • ambient-light-sensor
    Read ambient light
Media capture
  • camera
    Use the user's camera
  • microphone
    Use the user's microphone
  • autoplay
    Autoplay media
  • display-capture
    Screen-share via getDisplayMedia
  • speaker-selection
    Pick output audio device
Payment & auth
  • payment
    Use the Payment Request API
  • publickey-credentials-get
    Use WebAuthn
Display
  • fullscreen
    Programmatically go fullscreen
  • picture-in-picture
    Open PIP video
  • screen-wake-lock
    Keep the screen awake
  • xr-spatial-tracking
    Use WebXR spatial tracking
Other
  • geolocation
    Read user location
  • clipboard-read
    Read clipboard contents
  • clipboard-write
    Write to clipboard
  • usb
    Use Web USB
  • serial
    Use Web Serial
  • bluetooth
    Use Web Bluetooth
  • idle-detection
    Detect user idle
  • midi
    Use Web MIDI
  • web-share
    Use the Web Share API
Response header
Permissions-Policy: accelerometer=(), gyroscope=(), magnetometer=(), ambient-light-sensor=(), camera=(), microphone=(), autoplay=(self), display-capture=(), speaker-selection=(), payment=(), publickey-credentials-get=(), fullscreen=(self), picture-in-picture=(self), screen-wake-lock=(), xr-spatial-tracking=(), geolocation=(), clipboard-read=(), clipboard-write=(self), usb=(), serial=(), bluetooth=(), idle-detection=(), midi=(), web-share=(self)

Apply on every HTML response. To have browsers report violations to a collector, add a report-to parameter to the directives and a Reporting-Endpoints header (neither is generated here).

How to use

  1. Start with the strict preset (everything off) and turn on only what your page actually needs.
  2. self is the right answer for most page-level features. * is rarely the right answer — it lets every iframe use the feature.
  3. Custom mode takes an allowlist of origins (https://embed.example.com https://other.com). self and * are tokens, anything else needs the full origin.

About this tool

Show

Permissions-Policy tells the browser which powerful features your page and the frames inside it may use. Pick a feature and choose whether it is blocked outright, allowed for your own origin, allowed everywhere, or restricted to a list you write. Features are grouped by category so the one you meant to lock down is easy to find, and warnings flag the shapes that hand a capability to every embedded origin or leave a scheme off an entry in the allowlist.

The syntax is terser than it looks and easy to get subtly wrong. An empty list disables a feature everywhere, including your own page: camera=() means nobody, not "default". (self) allows the top-level document and same-origin frames, * allows any origin, and named origins go inside the parentheses in quotes with the scheme included — geolocation=(self "https://maps.example.com"). Dropping the quotes or the https:// does not raise an error; the entry is simply not understood, and the feature ends up more restricted than you intended.

The part that catches people is that this header is not the only gate. A cross-origin iframe also needs an allow attribute on the <iframe> element itself, and the two intersect: whatever the header permits, the frame still gets nothing unless the embedding tag asks for it. So camera=(self "https://widget.example") on the response and no allow="camera" on the tag produces a widget with no camera and no obvious reason why. Both sides have to agree.

Two more things worth knowing. A misspelled feature name is ignored silently rather than rejected, so a policy with a typo protects nothing and reports nothing — check the applied policy in the browser's developer tools rather than trusting the header you wrote. And this header replaced Feature-Policy, which used a different, space-separated syntax; an old example copied from a blog post will parse as nothing at all in a current browser, which again fails quietly.

Frequently asked questions

Permissions-Policy vs Feature-Policy?

Same idea, new syntax. Feature-Policy used a space-separated list of origins per directive; Permissions-Policy uses structured-headers feature=(origin1 origin2). Browsers honour Permissions-Policy now; serve both during the transition window if you support old browsers.

Why is wildcard * risky?

Wildcard hands the feature to every nested iframe — including third-party embeds you don't fully trust. If a malvertising or supply-chain compromise injects an iframe, * is the difference between 'they can spy with the camera' and 'they can't.'

A feature works on my page but breaks inside an iframe. Why?

Because a policy applies to the whole document tree, and an embedded frame starts with nothing unless the embedding page delegates the feature to it. Delegation takes both halves: the header has to allow the frame's origin, and the iframe tag has to carry the matching allow attribute. Missing either one leaves the frame denied while the top page works.

Does denying a feature here replace asking the user?

It removes the possibility rather than the prompt: a denied feature reports as unavailable, so the browser never asks and the page must handle the refusal. That is the useful property — it lets you guarantee that third-party code embedded on a page cannot reach the camera at all — but it is a policy about capability, not a consent mechanism, and code that assumes a permission dialogue will appear needs a path for the case where it does not.

Related tools