Guides Create account 🇬🇧 🇩🇪
  • Guides
  • Create account
  • Sign in
  • 🇬🇧 🇩🇪
  • Security guide

    Permissions-Policy, explained

    Camera, microphone, location and payment are available to every page in the browser until you say otherwise, and that includes anything your pages embed. This guide covers the allow-list syntax, the features worth naming, and the mistake that makes a header look set when it does nothing.

    Scan my headers All security headers

    01 Why it matters

    The permission nobody asked you about

    A browser will not hand out the camera without asking the visitor first, which makes Permissions-Policy look optional. The prompt is only half the story. It decides who may be asked, not who may ask, and by default everything running on your page may ask: your own scripts, a third-party widget, an advertising iframe, a chat tool that changed hands last year. Every one of them can put a permission dialog in front of your visitors with your domain in the title.

    The header moves that decision from the visitor to you. Features your site does not use are switched off before anything can ask for them, which removes the prompt and the risk that someone accepts it. It is the cheapest kind of hardening: nothing to maintain, and the safe state is the default.

    02 The syntax

    Four allow-lists to choose from

    camera=()

    What it does. An empty allow-list. The feature is off for your own pages and for every frame they embed, and no dialog can be triggered at all.

    Why it matters. This is the value for everything you do not use. It is not a request the browser might still grant: the API is simply not there.

    camera=(), microphone=()

    geolocation=(self)

    What it does. Allows the feature on your own origin only. Embedded frames from other origins are excluded even if they ask.

    Why it matters. The right value for a feature your own pages genuinely need. A compromised third-party embed still cannot reach it.

    geolocation=(self)

    payment=(self "https://pay.example.com")

    What it does. Allows your own origin plus the origins you name. Each origin is quoted and the entries are separated by spaces.

    Why it matters. This is how a payment or video provider gets what it needs without opening the feature to everything else you embed.

    payment=(self "https://pay.example.com")

    fullscreen=*

    What it does. Allows the feature everywhere, including in every embedded frame.

    Why it matters. Reasonable for something harmless such as fullscreen on a video page, and the wrong answer for anything that reaches a sensor or a wallet. If you are unsure, name the origins instead.

    fullscreen=*

    03 Rolling it out

    Name every feature you want off

    The header is one line in your server block, listing feature and value pairs separated by commas. A feature you do not list keeps the browser default, and that default usually allows your own origin, so a short header is not a strict one. Start from the features you actually use, switch off the rest by name, and reload.

    server { add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=()" always; }

    Check the live response rather than the configuration file. A browser silently ignores a feature name it does not know, so a typo looks exactly like a header that works. Our scan reads the header as it arrives.

    04 Questions

    What people ask about permissions

    Is this the same as the old Feature-Policy header?

    It replaces it. Feature-Policy used a different syntax and is no longer the standard; current browsers read Permissions-Policy. Sending only the old header means sending nothing that still counts today.

    Do I need it if my site never asks for the camera?

    That is exactly when it is cheapest. Your own code not asking says nothing about the third-party scripts and iframes on the same page. The header turns "we do not use it" into a rule the browser enforces rather than a statement of intent.

    What happens to an iframe whose feature is switched off?

    The API is unavailable inside it and the call fails as if the browser had no such feature. Nothing crashes, but a widget that genuinely needs the feature stops working, which is why you name the origins that should keep it.

    Does an unknown feature name break the header?

    No, the browser skips the entry it does not know and applies the rest. Convenient for new features, unhelpful for typos, so verify against the response instead of the file.

    05 In depth

    Related security topics

    Permissions decide what a page may reach. These guides cover what it may load and who may embed it.

    HTTP security headers

    What each response header does, which ones your site should send and how to configure them correctly.

    Read guide

    Content Security Policy

    The strongest defence against cross-site scripting: which directives to set, how to roll a policy out in report-only mode and how to reach an enforcing policy without breaking your site.

    Read guide

    HSTS

    Strict-Transport-Security explained: what max-age, includeSubDomains and preload actually do, and why the preload list is a decision you cannot quickly undo.

    Read guide

    Cookie security

    Secure, HttpOnly and SameSite: the cookie attributes that keep a session out of reach of scripts and cross-site requests, with the settings for a typical stack.

    Read guide

    Clickjacking and X-Frame-Options

    How an invisible overlay turns a visitor click into an action on your site, and the two headers that stop it: X-Frame-Options for older clients, frame-ancestors for everything else.

    Read guide

    Referrer-Policy

    Which part of your URLs travels to other sites when a visitor clicks away: the five policy values that matter, what each one gives up and the one to set by default.

    Read guide

    SPF, DMARC and DNSSEC

    The records that stop someone sending mail in your name, and the one that keeps your DNS answers honest: what each does and the order to introduce them in.

    Read guide

    security.txt

    The file that tells a researcher where to report a vulnerability. Two mandatory fields, ten minutes of work, and the difference between a private report and a public one.

    Read guide

    Monitoring security headers

    A header that is right today can be gone after the next deploy. Three ways to notice: a cron job with curl, a check in your CI pipeline and a scheduled scan, with what each of them catches and misses.

    Read guide

    Detecting CSP changes

    How a policy changes quietly through deploys, plugins and CDN rules, and how to catch it: a header diff, violation reports through report-to, and what a scan comparison can and cannot show.

    Read guide

    Keep reading

    Every guide stands on its own, and together they cover what our scanner looks at. Pick the one closest to your next question.

    Is your website affected? BFSG check Accessibility statement BFSG for online shops BFSG for medical practices BFSG for hotels BFSG for tradespeople HTTP security headers Privacy signals Content Security Policy HSTS Cookie security Clickjacking and X-Frame-Options Referrer-Policy SPF, DMARC and DNSSEC security.txt Monitoring security headers Detecting CSP changes Security headers in nginx Security headers in Apache Security headers in WordPress Security headers in IIS Security headers in TYPO3 Security headers in Shopware 6 Security headers in Plesk All guides

    See which features your site leaves open

    Our free Quickscan reads your Permissions-Policy from the live response alongside the rest of your security headers, and explains every finding in plain language.

    Scan a website Latest scans
    Recent scans Guides Local leagues Pricing Methodology For hosters API Data protection Imprint Accessibility Terms Cancel contracts here © 2026 Erseni