Zero Signup ToolsFree browser tools

Developer Tools

Permissions-Policy Generator

Build a valid Permissions-Policy HTTP header in your browser. Pick features, set allowlists, validate origins, get ready-to-paste server snippets.

Quick start

Pick a preset, then customize

Each preset enables a sensible set of features. You can refine every choice below.

Features

43 of 43 enabled.

Hardware sensors

Media and camera

Identity and payment

Browser features

Embedded content

Privacy and metrics

Permissions-Policy

Modern header

Structured Field syntax. Supported by Chrome 88+, Edge 88+, Firefox 74+, Safari 16.4+.

Permissions-Policy: accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()

Feature-Policy

Legacy header (optional)

The older syntax some browsers still recognize. Ship alongside the modern header for the broadest coverage.

Feature-Policy: accelerometer 'none'; ambient-light-sensor 'none'; battery 'none'; bluetooth 'none'; gamepad 'none'; gyroscope 'none'; hid 'none'; magnetometer 'none'; midi 'none'; serial 'none'; usb 'none'; autoplay 'self'; camera 'none'; display-capture 'none'; encrypted-media 'none'; fullscreen 'self'; microphone 'none'; picture-in-picture 'self'; screen-wake-lock 'none'; speaker-selection 'none'; xr-spatial-tracking 'none'; identity-credentials-get 'none'; payment 'none'; publickey-credentials-create 'none'; publickey-credentials-get 'none'; otp-credentials 'none'; clipboard-read 'none'; clipboard-write 'none'; geolocation 'none'; idle-detection 'none'; local-fonts 'none'; notifications 'none'; storage-access 'none'; window-management 'none'; cross-origin-isolated 'none'; execution-while-not-rendered 'none'; execution-while-out-of-viewport 'none'; fenced-unpartitioned-storage-read 'none'; web-share 'self'; attribution-reporting 'none'; browsing-topics 'none'; compute-pressure 'none'; interest-cohort 'none'

Static hosts

Meta tag fallback

Drop this inside the page head if you cannot set HTTP headers. Browser support for the meta form is more limited than the HTTP header.

<meta http-equiv="Permissions-Policy" content="accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()">

Validation

No issues detected

Your header is well-formed. Origins are validated; ( ), (self), and * are emitted exactly as the spec requires.

Server snippets

Drop the header into your stack

Each snippet sends the modern Permissions-Policy header on every response. Adjust paths and matchers to fit your routing.

Next.js (next.config.js)

JavaScript

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Permissions-Policy',
            value: 'accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()',
          },
        ],
      },
    ];
  },
};

Express middleware

JavaScript

app.use((req, res, next) => {
  res.setHeader(
    'Permissions-Policy',
    'accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()'
  );
  next();
});

Nginx

Nginx

add_header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()" always;

Apache .htaccess

Apache

Header always set Permissions-Policy "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()"

Vercel (vercel.json)

JSON

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Permissions-Policy",
          "value": "accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()"
        }
      ]
    }
  ]
}

Netlify (_headers)

Text

/*
  Permissions-Policy: accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()

Cloudflare Workers

JavaScript

export default {
  async fetch(request) {
    const response = await fetch(request);
    const headers = new Headers(response.headers);
    headers.set(
      'Permissions-Policy',
      'accelerometer=(), ambient-light-sensor=(), battery=(), bluetooth=(), gamepad=(), gyroscope=(), hid=(), magnetometer=(), midi=(), serial=(), usb=(), autoplay=(self), camera=(), display-capture=(), encrypted-media=(), fullscreen=(self), microphone=(), picture-in-picture=(self), screen-wake-lock=(), speaker-selection=(), xr-spatial-tracking=(), identity-credentials-get=(), payment=(), publickey-credentials-create=(), publickey-credentials-get=(), otp-credentials=(), clipboard-read=(), clipboard-write=(), geolocation=(), idle-detection=(), local-fonts=(), notifications=(), storage-access=(), window-management=(), cross-origin-isolated=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fenced-unpartitioned-storage-read=(), web-share=(self), attribution-reporting=(), browsing-topics=(), compute-pressure=(), interest-cohort=()'
    );
    return new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers,
    });
  },
};

Syntax reference

Allowlist values at a glance

feature=()

Empty allowlist. Blocks the feature everywhere, including in same-origin iframes.

feature=(self)

Allow only on this origin. Same-origin iframes inherit; cross- origin iframes do not.

feature=(self "https://x")

Allow on self and one or more specific origins. Origins are quoted with double quotes.

feature=*

Wildcard. Allows the feature on every origin including third-party iframes. Avoid for sensitive features.

How to use

  1. Pick a preset that is closest to your situation (Lock down everything, Allow on this origin only, Video call app, Site with map embed, Marketing or blog).
  2. Refine the result: tick the features you want to control, untick the ones you do not, and choose an allowlist mode for each (block, self only, origin list, or wildcard).
  3. For Origin list mode, paste one origin per line (https:// is added if you omit the scheme). Tick Also include self when self is one of the allowed origins.
  4. Read the validation panel: errors flag malformed origins, warnings flag risky patterns like wildcard star on camera or microphone.
  5. Copy the modern Permissions-Policy header for production, the legacy Feature-Policy header for broader fallback, or the meta tag for static hosts.
  6. Use any server snippet (Next.js, Express, Nginx, Apache, Vercel, Netlify, Cloudflare Workers) to drop the header straight into your stack.

About this tool

Permissions-Policy Generator builds a valid Permissions-Policy HTTP header for any combination of browser features. The Permissions-Policy header (formerly known as Feature-Policy) is the W3C standard for selectively enabling or disabling powerful browser APIs (camera, microphone, geolocation, fullscreen, autoplay, payment, sensors, WebHID, WebUSB, WebMIDI, screen wake lock, picture-in-picture, web share, clipboard, identity credentials, and many more) for a document and the iframes it loads. The tool exposes more than forty features from the W3C Permissions Registry, grouped into hardware sensors, media and camera, identity and payment, browser features, embedded content, and privacy and metrics, with a one-sentence description of what each feature actually controls so you do not have to look every name up in MDN. For each enabled feature you pick an allowlist: block (empty parentheses, the strongest form), self only, an explicit origin list with quoted origins (with an optional 'also include self' toggle so you do not have to repeat it), or wildcard star (allow everywhere, recommended only for low-risk features). Origins are validated with the URL constructor and serialized to scheme + host + optional port so a paste like example.com or example.com/path produces a clean https://example.com without surprises. The result panel shows three header forms side by side: the modern Permissions-Policy header using Structured Field syntax (RFC 8941), the older Feature-Policy header for browsers that still honour the legacy grammar, and a <meta http-equiv> tag for static hosts that cannot set HTTP headers. Five preset buttons cover the situations developers reach for most often: lock down everything, allow on this origin only, video call app (camera, microphone, display capture, fullscreen, picture-in-picture, screen wake lock, clipboard write), site with map embed (geolocation allowed on self plus a maps origin, fullscreen on self, everything else blocked), and a marketing or blog default (block sensors and payment, allow fullscreen and autoplay for media). Server snippets for Next.js, Express, Nginx, Apache, Vercel, Netlify, and Cloudflare Workers drop the header straight into your config. Validation surfaces the things that quietly break the header in production: trailing path or query on an origin (Permissions-Policy origins are scheme + host + port only), missing scheme that the tool auto-prefixes for you, an empty origin list that would silently degrade to a block, and the high-risk pattern of granting wildcard star to camera, microphone, geolocation, payment, or any device-access feature. Useful for hardening web apps, satisfying OWASP and CSP-style security audits, preparing for a third-party penetration test, complying with platform requirements (Google Workspace add-ons, browser extension hosting, kiosks), and shipping a defensible default policy on a fresh project. Everything runs locally on your device. No origins, no policies, no settings leave the browser tab.

Free to use. Works in your browser. No signup, no login.

Related tools

You may also like

All tools
All toolsDeveloper Tools