Browser Permissions & Fetch Metadata Headers
How Permissions-Policy restricts what browser features your page can use, and how Fetch Metadata headers give your server trustworthy request context.
Permissions-Policy: Restricting What Your Page Can Actually Do
Permissions-Policy lets a page explicitly declare which browser features — camera, microphone, geolocation, USB access, fullscreen, autoplay, and dozens more — it and any embedded iframes are permitted to use, regardless of whether the underlying browser API exists. This matters even for a page that never intentionally uses any of these features, because it closes off an entire category of risk: if a third-party script gets compromised (through a supply-chain attack on an analytics tag or ad script, for instance), a correctly configured Permissions-Policy prevents that compromised script from being able to invoke a sensitive API at all, since the policy blocks it at the browser level regardless of what the script's code actually tries to do.
The syntax structures around individual feature directives, each specifying an allowlist of origins permitted to use that specific feature. A directive like camera=() with empty parentheses disables camera access entirely, for every context including your own top-level page; camera=(self) permits it only for your own origin; and camera=(self "https://trusted-partner.com") extends that permission to one additional named origin, useful when you deliberately embed a trusted third-party widget that genuinely needs camera access.
camera=() and microphone=()) rather than trying to enumerate every feature — most sites use only a handful of these APIs, so denying by default and allowlisting the few you need is far less error-prone.Delegating Permissions to Embedded Iframes
A particularly useful application of Permissions-Policy is controlling exactly which embedded iframes can access which features, independent of what the iframe's own content might request. An embedded video conferencing widget genuinely needs camera and microphone access to function; an embedded ad iframe almost certainly doesn't, and restricting it via Permissions-Policy prevents that ad content (which you don't control and can't fully audit) from ever being able to request those permissions in the first place, regardless of what code it's running.
This can also be controlled per-iframe using the allow attribute directly on the <iframe> element, which works alongside the header-level policy — the two need to agree for a feature to actually be permitted, since the more restrictive of the two applies. This layered control is genuinely useful for sites embedding several different third-party widgets with different legitimate needs, letting each one be scoped precisely rather than applying one blanket policy to every embed on the page.
Fetch Metadata Headers: Letting Your Server See Request Context
Fetch Metadata headers — Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest, and Sec-Fetch-User — are automatically attached by the browser to every request it sends, and unlike most headers, they can't be set or overridden by JavaScript, making them a genuinely trustworthy signal about how a request was actually initiated. Sec-Fetch-Site tells your server whether the request came from the same origin, the same site (different subdomain), a cross-site origin, or was typed directly by the user (like a bookmark or address-bar navigation). Sec-Fetch-Mode indicates the request type — navigation, a fetch/XHR call, form submission, and similar. Sec-Fetch-Dest reveals what the requested resource will actually be used for — an image, a script, an iframe document, a worker, and more.
Because the browser sets these headers itself and doesn't allow application JavaScript to spoof them, they provide server-side code with reliable information that used to require fragile inference from the Referer header alone — information that's genuinely useful for making security decisions about how to handle a given request.
Using Fetch Metadata for Practical Server-Side Defense
A common, high-value pattern: rejecting cross-site requests to sensitive endpoints that should only ever be reached from your own site. If a request to your account-settings API arrives with Sec-Fetch-Site: cross-site, that's a strong signal something unusual is happening, since a legitimate user interacting with your own application would never generate a cross-site request to that specific endpoint under normal circumstances. This provides a genuinely useful additional layer of defense against certain cross-site attack patterns, without requiring the more invasive session-token-based protections that would otherwise be the primary defense.
Another practical use: distinguishing navigation requests from resource-loading requests using Sec-Fetch-Dest, letting a server serve different content or apply different caching rules depending on whether a request is for the actual HTML page versus an embedded resource on that page — a distinction that was previously difficult to make reliably server-side without this explicit signal.
The Full Set of Fetch Metadata Headers, Individually
Sec-Fetch-Site takes one of four values: same-origin, same-site, cross-site, or none (the last indicating the request wasn't triggered by another page at all, such as a direct URL entry or a bookmark). Sec-Fetch-Mode distinguishes navigate, cors, no-cors, and same-origin request types, reflecting how the browser itself is treating the request. Sec-Fetch-Dest covers a long list of specific destination types — document, image, script, style, iframe, worker, and many more — identifying precisely what role the fetched resource will play once loaded.
Sec-Fetch-User is narrower in scope, present only on navigation requests and set to ?1 specifically when the navigation was triggered by genuine user activation (a click or key press), rather than programmatically by script — a useful signal for distinguishing a real user-initiated page load from one triggered automatically, such as through a redirect chain or an auto-refreshing page.
Permissions-Policy vs Fetch Metadata: Different Layers, Complementary Roles
| Aspect | Permissions-Policy | Fetch Metadata Headers |
|---|---|---|
| Direction | Server tells browser what's allowed | Browser tells server what kind of request this is |
| Who acts on it | The browser, enforcing the restriction client-side | Your own server-side application logic |
| What it controls | Access to powerful browser APIs and features | Contextual information about request origin and intent |
| Can it be spoofed by page JavaScript? | N/A — it's a restriction, not spoofable data | No — set exclusively by the browser itself |
| Primary benefit | Prevents compromised scripts from using sensitive APIs | Enables smarter, more informed server-side security decisions |
Permissions-Policy's Relationship to the Older Feature-Policy Header
Permissions-Policy is the successor to an earlier header called Feature-Policy, which used a similar directive-based structure but was eventually renamed and refined as the specification matured — a shift driven partly by the "permissions" framing more accurately reflecting what the header actually controls, and partly by syntax refinements that came out of real-world implementation experience with the original. Feature-Policy still works in some older browser versions for backward compatibility, but current documentation and new deployments should use Permissions-Policy specifically, since that's where ongoing feature additions and refinements are happening.
Sites that adopted Feature-Policy early and haven't yet migrated aren't necessarily broken, but are missing newer directives and refinements that only exist under the Permissions-Policy name — a straightforward, low-risk update worth making during any broader header configuration review.
Real Scenarios for Adopting These Headers
A publisher embeds several third-party ad networks and wants to ensure none of them can ever request camera, microphone, or geolocation access, even if one of those networks is later compromised through a supply-chain attack. A Permissions-Policy denying these features by default across the entire page, with no per-origin allowlist exceptions, closes this off at the browser level regardless of what any embedded script attempts.
An API endpoint handling sensitive account changes starts rejecting requests where Sec-Fetch-Site indicates cross-site origin, adding a meaningful layer of defense against certain attack patterns targeting that endpoint, implemented as a straightforward server-side header check rather than requiring any client-side changes at all.
A video conferencing web app needs camera and microphone access for its own origin, embeds a separate analytics widget that needs neither, and configures Permissions-Policy to grant exactly the first and deny the second explicitly, rather than relying on the embedded widget simply choosing not to request permissions it technically could.
A security team investigating unusual traffic patterns uses Fetch Metadata's Sec-Fetch-Dest header to distinguish genuine page-navigation requests from a flood of iframe-embedding attempts from unrecognized origins, information that wasn't reliably available before these headers existed without more invasive request fingerprinting.
Combining Both Header Families for Layered Feature Control
Permissions-Policy and Fetch Metadata address different sides of the same broader goal — restricting what a page's content is capable of doing, and giving your server better information to make its own decisions — and combining them provides more complete coverage than either alone. A page might use Permissions-Policy to deny camera access to all embedded third-party content outright, while separately using Fetch Metadata on its own API endpoints to reject requests that don't originate from expected same-site navigation contexts, addressing client-capability restriction and server-side request validation as two distinct, complementary layers rather than expecting one mechanism to cover both concerns.
Neither header family is a substitute for foundational security practices like proper authentication, input validation, or CSRF tokens where applicable — they're additional, genuinely valuable layers that reduce the impact of certain failure modes (a compromised third-party script, an unexpected cross-site request) without replacing the need for those foundational protections in the first place.
Rolling These Out Without Breaking Existing Functionality
For Permissions-Policy, the safest rollout approach is auditing which features your site and its embedded content actually use before writing any restrictive policy — browser developer tools typically log a warning when a feature is blocked by policy, making it straightforward to identify gaps during a testing period before deploying restrictively in production. For Fetch Metadata-based server logic, start by logging the header values you're seeing in production traffic without actually rejecting anything, to understand your real traffic patterns (including legitimate but less obvious cases, like requests from browser extensions or accessibility tools) before adding any enforcement logic that could reject genuine users.
Checking What Your Site Currently Sends
Verifying whether Permissions-Policy is actually present, and correctly scoped, on your live site requires checking the real HTTP response rather than assuming from your application's configuration files — a value that looks correct in your server config can still be stripped or altered by an intermediate proxy or CDN before reaching an actual visitor's browser. Our Security Headers Checker and HTTP Headers Checker both show the live headers a domain is actually serving, confirming exactly what a real request receives.
FAQ
ToolsNovaHub guides are researched against primary sources (RFCs, vendor docs) and kept up to date as standards change. Spotted an error? Let us know.
📋 Related Tools & Guides Comparison
| Resource | Type | Link |
|---|---|---|
| Security Headers Checker | Tool | Open Tool → |
| HTTP Headers Checker | Tool | Open Tool → |
| Cross-Origin Isolation Headers Guide | Guide | Read Guide → |
| Security Headers Explained | Guide | Read Guide → |