Permissions Policy Generator

Switch off what your pages never use.

  • Feature by feature
  • iframe allow included
  • Parses any header
A wall-mounted panel of rows of unlabelled toggle switches with small indicator lamps, some of them lit, in a workshop.

The guide

A web page can give up powers it will never use, and one response header is how it says so

Permissions Policy lets a site switch off the camera, the microphone, location and dozens of other features for itself and for every frame it embeds. It is easy to write wrongly.

  • 16 April 2019 first public draft, under the name Feature Policy
  • 16 July 2020 first draft published as Permissions Policy
  • February 2021 RFC 8941, the syntax the header is written in
  • 81 feature names in the registry file read for this page
Read the full guide Fold the guide 7 min · 16 sources

Most web pages will never open a camera. They will never ask where the reader is standing, pair with a serial port, or take a payment. Yet every one of those abilities sits ready in the browser, available to any script the page happens to load, including the ones its author did not write.

That is the gap a permissions policy closes. A site sends one response header listing features and who may use them, and the browser holds the page to it.

The idea is older than the web: do not carry powers you have no use for. What follows is how the mechanism came to look the way it does, how it decides, and where its promises stop.

Why a page would disarm itself

The specification gives its reasons plainly. A developer may want to disable features to lock an application down, as a security or performance precaution, so that neither their own code nor third-party content inside the page can introduce unwanted behaviour. A developer may equally want to switch on something that is off by default in embedded content. And a policy can serve as a promise to whoever embeds the page.

The first reason is the one that matters to most sites. A modern page is assembled from an analytics script, a consent banner, a chat widget, a font loader and whatever those pull in after them. Each runs with the page’s own authority. If one of them is compromised, or simply changes its mind about what it collects, it can reach for the microphone exactly as the page itself could. A policy that sets the microphone to nobody removes the question.

Frames sharpen the point. An embedded map, video player or advert is somebody else’s document inside yours. The older tool for this, the sandbox attribute, takes everything away and makes the author opt back in, item by item, which the specification notes is hard to extend without breaking existing pages. A permissions policy works the other way round: features are named one at a time, and it is meant to be used alongside the sandbox, not instead of it.

From Feature Policy to Permissions Policy

The mechanism first appeared in public as Feature Policy, whose First Public Working Draft is dated 16 April 2019. Its header was a plain string. A site wrote Feature-Policy: geolocation 'self' https://maps.example.net, a feature name followed by quoted keywords and bare origins, with semicolons between features.

Fifteen months later the next draft, dated 16 July 2020, carried a new title, and its change log records three things at once: the rename to Permissions Policy, a header redefined as a structured field, and a change in how the header and the frame attribute combine, from either one sufficing to both being required. The current draft still opens with a single sentence of acknowledgement, that it used to be named Feature Policy.

The new header is written in Structured Field Values, a general syntax for HTTP headers that the IETF published as RFC 8941 in February 2021 and revised as RFC 9651 in September 2024. The same policy became Permissions-Policy: geolocation=(self "https://maps.example.net"): a dictionary of feature names, each with a list in parentheses, the word self without quotes and every origin inside double quotes. The frame attribute kept the older spelling, so the two halves of one policy are written in two different notations to this day.

That difference is where most mistakes are made. Structured fields are deliberately strict: if a value fails to parse, the whole field must be ignored, as if it had not been sent. Paste the old syntax into the new header, or wrap self in single quotes from habit, and the page silently has no policy at all.

A quieter error is worse. In this syntax a bare word may contain colons and slashes, so an origin typed without its double quotes still parses. It becomes a token, and the specification says any item in the list other than a quoted string, self or the star is skipped. The header looks right, the browser accepts it, and the origin has not been allowed.

Allowlists, defaults and frames

Each feature in a policy gets an allowlist, which is conceptually a set of origins. An empty list means nobody, here or in any frame below. The star means every origin. In between come self, the page’s own origin, and explicit origins, which may use a wildcard for subdomains or for ports. The specification’s own example adds a caution: an entry for every subdomain of a site does not cover the site’s bare domain, which needs an entry of its own.

Features a policy does not mention fall back on a default allowlist, set by whichever specification defines the feature. There are two kinds. A default of star means the feature works in the page and in every frame unless someone turns it off. A default of self means it works in the page and in same-origin frames, and is off in cross-origin frames until the page hands it over. Geolocation, the camera and the microphone are all defined with a default of self.

Handing over is the job of the allow attribute on the frame. Written as allow="geolocation", with no origin after the name, it delegates the feature to the origin of the frame’s own address. The attribute is read when the frame is navigated, so changing it afterwards does nothing to a document that has already loaded.

The decision for a frame runs in a fixed order. If the feature is disabled in the embedding page, it is disabled in the frame. If the page’s header declares the feature and its allowlist does not include the frame’s origin, it is disabled in the frame. Only then is the attribute consulted, and after it the default. The HTML standard states the consequence in one line: the attribute cannot grant a feature that the embedding document is not itself allowed to use.

A worked case makes the order concrete, and it can be typed into the simulator on this page exactly as written. A page at https://www.example.org/ sends fullscreen=(self "https://video.example.net") and embeds a player from https://video.example.net/embed/42 with allow="fullscreen". Full screen is enabled in the page and in the frame. Shorten the header to fullscreen=(self) and leave the attribute alone: the frame loses it, because the header no longer lists its origin. Restore the header and delete the attribute instead: the frame loses it again, because the default of self does not reach across origins. Both halves are needed, and the generator on this page shows each step with its section number.

Inheritance continues downward. A frame given the camera may pass it on with another attribute, and may narrow it with its own header. It cannot widen what it received.

What the header does not do

It is not a permission prompt. Allowing a feature in a policy grants nothing; it decides only whether the page may ask. The W3C specification on permissions puts the relationship precisely: for a powerful feature that is also policy-controlled, the policy governs whether the feature is available at all, and only then can the document request the user’s express permission. A reader who was going to be asked is still asked.

It is not a substitute for other headers either. Where scripts may load from is the work of Content Security Policy, from which it borrows only a way of matching origins.

Nor is it uniform. The specification says a browser has a set of supported features and is not required to support every one, and that a name it does not recognise is ignored. The list of names lives in a separate, non-normative registry file, sorted into standardized, proposed, experimental and retired. The copy read for this page, last changed on 26 May 2026, holds 81 names, and it changes. A policy written from an old list leaves newer features at their defaults.

There is one more limit that cuts the other way. A policy lets an embedding page disable features in a document that never expected to lose them. The specification’s security section walks through a contrived login check that fails open when synchronous requests are switched off from outside, and its advice is to detect features before relying on them.

Adopting it without breaking the site

Begin with what the site uses, not with a list of everything. Search the code and the embedded widgets for camera, location, payment and full-screen calls, and write those down with the origins that need them. Everything else that the registry marks as standardized is a candidate for the empty list.

Then send the header to a small share of pages and watch. The draft describes a report-only variant of the header and a reporting parameter for exactly this purpose, though support for both should be tested, not assumed. Check embedded players and maps by hand, since a frame fails quietly.

Mind where the header is set. The manuals of two common open-source servers both note that, by default, a configured header is left off error responses; each needs the extra word always before those pages carry it too. And because the header and the frame attributes are separate texts in separate syntaxes, change them together. A policy is a short piece of text. Its value is that the page keeps to it even when everything loaded into the page does not.

Sources

Every source consulted for this guide. Links checked October 10, 2026.

  1. Permissions Policy World Wide Web Consortium (W3C), Working Draft, 22 September 2026
  2. Permissions Policy World Wide Web Consortium (W3C), Working Draft, 16 July 2020
  3. Feature Policy World Wide Web Consortium (W3C), First Public Working Draft, 16 April 2019
  4. Permissions Policy publication history World Wide Web Consortium (W3C)
  5. Policy Controlled Features W3C Web Application Security Working Group repository (W3C Software and Document License)
  6. Structured Field Values for HTTP RFC 8941, Internet Engineering Task Force
  7. Structured Field Values for HTTP RFC 9651, Internet Engineering Task Force
  8. Content Security Policy Level 3 World Wide Web Consortium (W3C), Working Draft, 16 September 2026
  9. HTML Standard WHATWG, section 4.8.5 The iframe element
  10. Permissions World Wide Web Consortium (W3C), Working Draft, 6 October 2025
  11. Geolocation World Wide Web Consortium (W3C), Candidate Recommendation Snapshot, 26 March 2026
  12. Media Capture and Streams World Wide Web Consortium (W3C), Candidate Recommendation Draft, 9 October 2025
  13. Permissions Policy MDN Web Docs (CC BY-SA)
  14. Permissions-Policy header MDN Web Docs (CC BY-SA)
  15. Apache Module mod_headers The Apache HTTP Server Project, version 2.4 documentation (Apache License 2.0)
  16. Module ngx_http_headers_module nginx documentation (2-clause BSD licence)