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
- 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).
- 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).
- 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.
- Read the validation panel: errors flag malformed origins, warnings flag risky patterns like wildcard star on camera or microphone.
- Copy the modern Permissions-Policy header for production, the legacy Feature-Policy header for broader fallback, or the meta tag for static hosts.
- 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
CSP Header Generator
Visual builder for the Content-Security-Policy HTTP header.
Open tool
DeveloperCORS Headers Generator
Build Access-Control headers with live validation and Apache, Nginx, Vercel, Netlify, Next.js, Worker, and Express snippets.
Open tool
DeveloperCache-Control Header Builder
Build and parse Cache-Control headers with directive flags, max-age presets, conflict checks, and ready-to-paste server snippets.
Open tool
DeveloperSet-Cookie Builder
Form-driven Set-Cookie header builder with conflict warnings and server snippets.
Open tool
DeveloperHTTP Headers Parser
Parse, classify, and decode HTTP headers, with a missing security headers audit.
Open tool
GeneratorHTML Iframe Generator
Build accessible, responsive iframe code with sandbox, allow, and referrerpolicy attributes.
Open tool