How HTTP Works

Comparison

Permissions-Policy vs Feature-Policy

Understand the differences between Permissions-Policy and Feature-Policy headers. Compare syntax changes for geolocation, microphone, camera directives and learn how to migrate.

Bottom line: Permissions-Policy is the current header. Feature-Policy is the deprecated older form. The feature names are familiar, but the syntax changed enough that copy-paste migrations often break silently.

By How HTTP WorksReview process
Permissions-Policy
vs
Feature-Policy

TL;DR: Permissions-Policy is the current header. Feature-Policy is the deprecated older form. The feature names are familiar, but the syntax changed enough that copy-paste migrations often break silently.

The Fastest Way To Think About This

If you are migrating old security headers, the important point is not “which features do these headers control?” It is:

The Core Difference

Both headers control browser capabilities like geolocation, microphone, and camera for a page and its embedded contexts. Feature-Policy is the deprecated original. Permissions-Policy is the current replacement with structured syntax.

Permissions-Policy uses structured field values with parenthesized allowlists:

Permissions-Policy: geolocation=(self), microphone=(), camera=(self "https://video.example.com")

Feature-Policy used semicolon-separated directives with quoted keywords:

Feature-Policy: geolocation 'self'; microphone 'none'; camera 'self' https://video.example.com

The feature names (geolocation, microphone, camera, etc.) are identical — only the syntax around them changed.

Syntax Comparison

AspectPermissions-PolicyFeature-Policy
Directive separator, (comma); (semicolon)
Block all originsfeature=()feature 'none'
Allow all originsfeature=*feature *
Same origin onlyfeature=(self)feature 'self'
Specific originfeature=("https://example.com")feature https://example.com
Multiple originsfeature=(self "https://a.com")feature 'self' https://a.com
Assignment syntaxfeature=(allowlist)feature allowlist

The biggest change is the move from quoted keywords ('self', 'none') to structured values ((self), ()).

Geolocation

The most commonly searched feature. Here’s how the syntax maps:

Block Geolocation Entirely

# Permissions-Policy (current)
Permissions-Policy: geolocation=()

# Feature-Policy (deprecated)
Feature-Policy: geolocation 'none'

Allow Geolocation for Same Origin

# Permissions-Policy (current)
Permissions-Policy: geolocation=(self)

# Feature-Policy (deprecated)
Feature-Policy: geolocation 'self'

Allow Geolocation for Specific Origins

# Permissions-Policy (current)
Permissions-Policy: geolocation=(self "https://maps.googleapis.com")

# Feature-Policy (deprecated)
Feature-Policy: geolocation 'self' https://maps.googleapis.com

Microphone

Block Microphone Access

# Permissions-Policy (current)
Permissions-Policy: microphone=()

# Feature-Policy (deprecated)
Feature-Policy: microphone 'none'

Allow Microphone for Video Chat

# Permissions-Policy (current)
Permissions-Policy: microphone=(self "https://meet.example.com")

# Feature-Policy (deprecated)
Feature-Policy: microphone 'self' https://meet.example.com

Camera

Block Camera Access

# Permissions-Policy (current)
Permissions-Policy: camera=()

# Feature-Policy (deprecated)
Feature-Policy: camera 'none'

Allow Camera for Same Origin

# Permissions-Policy (current)
Permissions-Policy: camera=(self)

# Feature-Policy (deprecated)
Feature-Policy: camera 'self'

Multiple Features

Real-world policies typically combine multiple feature directives. Here’s a full migration example:

Feature-Policy (deprecated)

Feature-Policy: geolocation 'self'; microphone 'none'; camera 'none'; payment 'self' https://checkout.stripe.com; autoplay 'self'

Permissions-Policy (current)

Permissions-Policy: geolocation=(self), microphone=(), camera=(), payment=(self "https://checkout.stripe.com"), autoplay=(self)

Migration Checklist

  1. Replace the header name — Feature-Policy → Permissions-Policy
  2. Change separators — ; → ,
  3. Add =() assignment — feature allowlist → feature=(allowlist)
  4. Replace 'none' — 'none' → ()
  5. Replace 'self' — 'self' → self (no quotes, inside parentheses)
  6. Quote specific origins — https://example.com → "https://example.com" (inside parentheses)
  7. Test in DevTools — verify features are correctly allowed or blocked
  8. Remove Feature-Policy — once you no longer need legacy browser support

Server Configuration

Nginx Migration

# Before (deprecated)
add_header Feature-Policy "geolocation 'self'; microphone 'none'; camera 'none'";

# After (current)
add_header Permissions-Policy "geolocation=(self), microphone=(), camera=()";

Apache Migration

# Before (deprecated)
Header always set Feature-Policy "geolocation 'self'; microphone 'none'; camera 'none'"

# After (current)
Header always set Permissions-Policy "geolocation=(self), microphone=(), camera=()"

Express.js Migration

// Before (deprecated)
app.use((req, res, next) => {
  res.set(
    "Feature-Policy",
    "geolocation 'self'; microphone 'none'; camera 'none'",
  );
  next();
});

// After (current)
app.use((req, res, next) => {
  res.set("Permissions-Policy", "geolocation=(self), microphone=(), camera=()");
  next();
});

Browser Support

BrowserPermissions-PolicyFeature-Policy
Chrome88+60+ (legacy, still recognized)
Edge88+79+ (legacy)
FirefoxPartial (subset of features)See MDN compatibility table
Safari15.4+ (partial)See MDN compatibility table
Opera74+47+ (legacy)

Feature-Policy is deprecated and no longer developed, and its syntax differs, so a site that sends only the old header will not get any of the newer Permissions-Policy features (for example origin allowlists in the structured syntax). Send Permissions-Policy.

Common Mistakes

Mixing old and new syntax — using Permissions-Policy: geolocation 'self' (Feature-Policy syntax with the Permissions-Policy header name) is silently ignored by browsers. The value must use the new structured field syntax.

Forgetting to quote origins — in Permissions-Policy, specific origins must be quoted: geolocation=(self "https://example.com"). Unquoted origins are silently ignored.

Using semicolons as separators — Permissions-Policy: geolocation=(self); microphone=() is incorrect. Use commas: Permissions-Policy: geolocation=(self), microphone=().

Only sending Feature-Policy — if you only send Feature-Policy, modern Chromium browsers ignore it completely. Current protection requires Permissions-Policy.

FAQ

Is Feature-Policy deprecated?

Yes. Feature-Policy was renamed to Permissions-Policy. Chromium shipped Feature-Policy first (Chrome 60) and has kept recognizing the old header alongside Permissions-Policy, but the spec is deprecated and new code should send Permissions-Policy only. Check MDN’s compatibility table before depending on Feature-Policy in any browser.

What changed between Feature-Policy and Permissions-Policy?

The syntax changed significantly. Semicolons became commas as separators. Quoted keywords like ‘self’ and ‘none’ became unquoted self and empty parentheses (). Space-separated allowlists are now wrapped in parentheses. Feature names stayed the same.

How do I migrate geolocation from Feature-Policy to Permissions-Policy?

Change Feature-Policy: geolocation ‘self’ to Permissions-Policy: geolocation=(self). To block entirely, change geolocation ‘none’ to geolocation=(). To allow specific origins, change geolocation ‘self’ https://maps.example.com to geolocation=(self “https://maps.example.com”).

How do I migrate microphone and camera from Feature-Policy to Permissions-Policy?

Change Feature-Policy: microphone ‘none’; camera ‘none’ to Permissions-Policy: microphone=(), camera=(). The pattern is the same for all features — replace ‘none’ with (), ‘self’ with (self), and wrap origin lists in parentheses.

Can I use both Feature-Policy and Permissions-Policy at the same time?

Yes, but it is not recommended. If both headers are present, Permissions-Policy takes precedence in browsers that support it. Older browsers that only understand Feature-Policy will use that header. During migration, you can send both temporarily, but remove Feature-Policy once you no longer need to support very old browsers.

Do all browsers support Permissions-Policy?

Chrome and Edge 88+ and Opera 74+ support the Permissions-Policy header. Firefox and Safari support it with a smaller set of recognized features, and unknown feature names are ignored. Support differs per feature, so check the MDN compatibility table for the specific directives you set.

References

Browse /search