Permissions Policy Explained: Checking Which Browser Features Your Site Allows

Modern browsers expose powerful features to web pages: the camera, the microphone, geolocation, payment requests, and more. A page that embeds third-party content, such as an advertisement, a widget, or a video, may grant those features to the embedded content without meaning to, simply because the defaults allow it. Permissions Policy is the mechanism that lets a site declare, in an HTTP header, which features are available to its own pages and to embedded frames, and from which origins. The Permissions Policy Checker reads a policy you supply and breaks it into its directives, so you can see exactly what is granted and to whom. It analyses the policy text you supply; it does not fetch your site or prove the policy is enforced, since enforcement depends on the browser and on the header actually being served.

The policy is written as a list of directives, each naming a feature and the origins allowed to use it. A directive can allow a feature for the site itself, for specific origins, or for no one at all, and the difference between those three is the whole point. Allowing the camera for your own origin means your own pages may request it; allowing it for all origins means any embedded frame may request it too, which is rarely intended. Disabling a feature entirely, by listing it with an empty allowlist, is the safest choice for features the site never uses, because a feature that is disabled cannot be abused by compromised third-party content.

How it works

A checker parses the header value into directives, and for each directive it lists the feature, the allowed origins, and whether the directive disables the feature, restricts it to the site itself, or opens it more widely. Useful checkers also flag common problems: a feature allowed for all origins, a directive for a feature the site does not use, a malformed origin, or a directive that uses an older syntax the browser may interpret differently. The output is a readable inventory of grants, which is far easier to review than the raw header, especially when the policy contains a dozen or more directives.

How it works

The review question for each directive is simple: does this site, or a specific trusted embed, actually need this feature? A video-conferencing site needs the camera and microphone for its own origin; a blog does not, and a blog that leaves them enabled for all origins is granting capabilities it never uses to every embedded frame on its pages. The same logic applies to geolocation, payment, and less obvious features such as the ability to read the clipboard or to use the device's sensors. Unused grants are pure risk, and removing them costs nothing, because nothing on the site was using the feature in the first place.

Worked example

Worked example with sample values. Suppose a sample policy contains 6 directives: camera allowed for the site itself only, microphone allowed for the site itself only, geolocation disabled entirely, payment disabled entirely, fullscreen allowed for the site itself and one video origin, and clipboard-read disabled entirely. The checker runs 4 checks per directive in principle, but reports at the policy level: 0 directives allow a feature for all origins, 2 directives disable a feature entirely, 3 directives restrict a feature to the site itself or a named origin, and 1 directive, fullscreen, allows a named third-party origin. That is 6 directives reviewed, with 0 high-risk grants. If the camera directive had instead allowed all origins, the count of high-risk grants would rise from 0 to 1, and the fix would be to restrict it to the site itself. These are sample values for illustration.

Worked example

The same example shows why the named origin in the fullscreen directive deserves a second look. Suppose the video origin is a provider the site embeds on 40 pages, and the fullscreen grant is needed for the video player to go fullscreen. The grant is justified, but it should be reviewed when the provider changes or when the embed is removed: a grant left behind after its embed is gone becomes an unused grant, and unused grants are exactly the ones that should be removed. Keeping a short note of why each third-party grant exists, alongside the policy, makes that review quick, and the review should happen whenever embeds change, not only when something breaks.

Mistakes to avoid

One common mistake is copying a policy from another site without understanding its directives, which imports grants the site does not need and may block features it does need. Another is writing the policy but never serving it, for example testing it in a document and forgetting to configure the server header, so the browser never sees it. A third is using the older Feature-Policy syntax alongside the newer Permissions-Policy syntax with conflicting values, which creates confusion about which policy actually applies. Serve one policy, in the current syntax, from the server, and verify it in the live response headers.

Mistakes to avoid

A further mistake is treating the checker output as proof of enforcement. A checker reads the policy text you give it, not the header your server sends, and a policy that is correct in a document but absent from the server protects nothing. Always verify the live response headers after deployment, and re-check after server or framework changes, since a migration can silently drop custom headers. Also remember that Permissions Policy controls browser features, not server-side access: it does not replace authentication, authorisation, or input validation, and a site that relies on it for those purposes is unprotected where it matters most.

Putting it into practice

Add a policy review to the site's launch and embed-change routines: list every feature the site and its embeds genuinely need, write the policy to grant exactly those, disable everything else, and check the result with a checker before deployment. Verify the live headers after deployment, and record the reason for each third-party grant so future reviews are fast. A tight Permissions Policy is a small header with an outsized effect: it turns the browser's powerful features from a default-open risk into a deliberately-granted set of capabilities, and that deliberate stance is what good site security looks like.

Related tools and reading: Related tools and reading: the HTTP Headers Checker and the Referrer Policy Checker cover neighbouring checks that pair naturally with this one, and the guide at [/blog/http-headers-checker-guide/] extends the same topic with further background and examples for readers who want to go deeper.

Key points

  • Permissions Policy declares which browser features pages and embeds may use
  • Disable features the site never uses, and restrict the rest to named origins
  • Allowing a feature for all origins is the highest-risk grant to watch for
  • Verify the live server headers, since a checker reads text, not enforcement

In short: p.ermissions Policy declares which browser features pages and embeds may use

One habit ties all of this together: re-check after every change. A result is only as current as the input it was run on, so a fix, a redesign, or a new version of the underlying material deserves a fresh run rather than reliance on an old report. Keep the previous result alongside the new one, note what changed between them, and treat any new finding as a question to investigate rather than a verdict to accept or dismiss. That loop of checking, fixing, re-checking and recording is what turns a one-off test into a reliable routine, and it costs far less time than debugging a problem that a second check would have caught early.