HTTP

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 modern replacement for the deprecated Feature-Policy header. The syntax changed from semicolon-separated directives with quoted keywords to comma-separated directives with parenthesized allowlists.

Permissions-Policy
vs
Feature-Policy

The Core Difference

Both headers control which browser features (geolocation, microphone, camera, etc.) a page and its embedded iframes can use. Feature-Policy is the deprecated original; Permissions-Policy is the modern replacement with a different syntax.

Permissions-Policy uses structured field values with parenthesized allowlists:

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

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

```http
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'
```text

### Allow Geolocation for Same Origin

```http
# 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
```text

## Microphone

### Block Microphone Access

```http
# 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
```text

## Camera

### Block Camera Access

```http
# 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'
```text

## Multiple Features

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

### Feature-Policy (deprecated)

```http
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)
```text

## 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

```nginx
# 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=()"
```text

### Express.js Migration

```javascript
// 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–87
Edge88+79–87
Firefox74+ (partial)74+ (partial)
Safari15.4+ (partial)Not supported
Opera74+47–73

Chrome dropped Feature-Policy support entirely in version 88, so any site still sending only Feature-Policy headers gets no protection in modern Chrome.

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 separatorsPermissions-Policy: geolocation=(self); microphone=() is incorrect. Use commas: Permissions-Policy: geolocation=(self), microphone=().

Only sending Feature-Policy — if you only send Feature-Policy, modern Chrome and Edge ignore it completely. You must send Permissions-Policy for current browser coverage.