Rails 6.1 adds HTTP Feature Policy


What is Feature Policy?

Feature Policy allows us to control the behavior of certain web APIs and features in the browser. We can specify which APIs are to be disabled or enabled for a given list of origins.

Feature Policy provides two ways to do this:

  • Feature-Policy HTTP header
Feature-Policy: camera 'none' 

Feature-Policy: payment 'self' 'https://example.com'
  • allow attribute on iframes (Not supported by this change)
<iframe src='https://example.com' allow='fullscreen'></iframe> 

<iframe src='https://example.com' allow='geolocation *'></iframe>

Google Dev Blog, has an in-depth article explaining this functionality in detail.

Why should we use it?

It allows us to enforce similar UX throughout the application by enforcing the policies. In case a third party attempts an unexpected action, the browser will adhere to the policies provided by us and maintain the intended UX.

Policies are a contract between developer and browser. They inform the browser about what the developer’s intent is and thus, help keep us honest when our app tries to go off the rails and do something bad. If the site or embedded third-party content attempts to violate any of the developer’s preselected rules, the browser overrides the behavior with better UX or blocks the API altogether.

Let’s configure it in rails

Rails 6.1 adds a DSL for configuring the Feature-Policy HTTP header. The DSL is almost identical to that of configuring Content Security Policy.

We can define a policy with global scope in an initializer:

# config/initializers/feature_policy.rb 

Rails.application.config.feature_policy do |f|
  f.camera       :none
  f.geolocation  :self, 'https://example.com'
  f.payment      :self, '*' 
end

Let us see what is the value of the Feature-Policy response header in this case.

Feature-Policy Header value Feature-Policy Header value

This example would do the following:

  • Usage of camera API will be disallowed for all browsing contexts.
  • Usage of geolocation API will be disallowed for all browsing contexts except the page’s own origin and https://example.com.
  • Usage of payment API will be allowed for all browsing contexts.

We can also override the global policy within a controller:

# app/controllers/profiles_controller.rb 

class ProfilesController < ApplicationController
  def edit
    feature_policy do |f|
      f.camera  :self
    end
  end
end

This example would disallow usage of camera API for all browsing contexts(origin and third party extension) except the page’s own origin but only on ProfilesController#edit. This behavior will override any policies written in the global policy for the camera feature.

Let’s see this in action

We will first set a Feature Policy in the initializer like the example above.

# config/initializers/feature_policy.rb 

Rails.application.config.feature_policy do |f|
  f.geolocation  :none
end

Next up, we open up a page in our application where we have a map. The map shows the user’s current location. This is done via a script, which uses geolocation API.
Let’s see what happens.

Geolocation disabled Geolocation disabled

The script was not able to access the geolocation API because of the policy set above.

Here’s a more detailed reason why this happened.

Without Feature Policy
Console without Feature Policy Console without Feature Policy
With Feature Policy
Console with Feature Policy Console with Feature Policy

The Feature Policy we enforced on the browser, didn’t let the script access the geolocation API.

Can we use it yet?

See caniuse.com for the latest details on browser support.

As of today, the HTTP Feature-Policy header is supported by Chrome, Edge, Opera, and some mobile browsers.