Permissions Policy
Control which browser features (camera, geolocation, autoplay) are available to your page and embedded iframes using Permissions Policy.
Overview
Key Points
- •HTTP header: `Permissions-Policy: camera=(), microphone=()` disables for all origins
- •`feature=*` allows for any origin; `feature=self` allows only same-origin; `feature=()` blocks everywhere
- •Iframe attribute: `allow="camera; microphone; fullscreen"` grants listed features to the iframe
- •Iframe `allow="camera 'src'"` grants only the iframe's src origin
- •The `<meta http-equiv="Permissions-Policy">` approach is NOT supported use HTTP headers or iframe allow
- •Permissions API (`navigator.permissions.query`) lets JS check if a feature is granted/denied before requesting it
- •Violations are observable via `Reporting API` with `report-to` directive
Examples
HTTP Permissions-Policy header configuration
<!-- These go in your server configuration, not HTML -->
<!-- Example: Next.js next.config.ts headers -->
// next.config.ts
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Permissions-Policy',
// camera: nobody gets it
// geolocation: only this origin
// fullscreen: anyone (CDN iframes, etc.)
// autoplay: blocked
value: 'camera=(), microphone=(), geolocation=(self), fullscreen=*, autoplay=()'
}
]
}
];
}
};The HTTP header applies to the page AND all its embedded iframes it is the most powerful control.
Controlling iframe permissions with the allow attribute
<!-- ✅ Embed a video player allow fullscreen and autoplay only -->
<iframe
src="https://player.vimeo.com/video/12345"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
width="640"
height="360"
title="Product demo video"
></iframe>
<!-- ✅ Embed a map allow geolocation for this iframe's origin only -->
<iframe
src="https://maps.example.com/embed"
allow="geolocation 'src'"
title="Store location map"
></iframe>
<!-- ✅ Embed a payment widget allow payment API -->
<iframe
src="https://checkout.stripe.com/payment"
allow="payment"
title="Secure payment"
></iframe>
<!-- ❌ DANGEROUS: no allow restrictions at all -->
<!-- <iframe src="https://ads.example.com/banner"></iframe> -->Only grant features that the iframe actually needs. An ad iframe should not have camera or microphone.
Querying permission status from JavaScript
// Check if camera permission is granted before requesting it
async function checkCameraPermission() {
try {
const status = await navigator.permissions.query({ name: 'camera' });
switch (status.state) {
case 'granted':
startCamera();
break;
case 'prompt':
// Will ask user show UI explaining why we need camera
showCameraExplanation();
break;
case 'denied':
// Blocked by user or Permissions-Policy
showCameraBlockedMessage();
break;
}
// Listen for changes (user grants/revokes in browser settings)
status.addEventListener('change', () => {
console.log('Camera permission changed to:', status.state);
});
} catch (err) {
console.error('Permission query failed:', err);
}
}Common Mistakes
- Trying to set Permissions-Policy via a <meta> tag this is not supported. Only HTTP response headers and iframe allow attributes work
- Using `allowfullscreen` attribute WITHOUT corresponding `allow="fullscreen"` modern browsers require both for full compatibility
- Granting all features with `allow="*"` in iframes this is equivalent to no policy; grant only what the iframe needs
Note
Use Chrome DevTools > Application > Permissions Policy to see which features are allowed or blocked on the current page and each frame. This makes debugging policy conflicts much easier.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4