How to override X-Frame-Options for a controller or action in Rails 4

Ruby on-RailsIframeHttp HeadersRuby on-Rails-4X Frame-Options

Ruby on-Rails Problem Overview


Rails 4 appears to set a default value of SAMEORIGIN for the X-Frame-Options HTTP response header. This is great for security, but it does not allow for parts of your app to be available in an iframe on a different domain.

You can override the value of X-Frame-Options globally using the config.action_dispatch.default_headers setting:

config.action_dispatch.default_headers['X-Frame-Options'] = "ALLOW-FROM https://apps.facebook.com"

But how do you override it for just a single controller or action?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

If you want to remove the header completely, you can create an after_action filter:

class FilesController < ApplicationController
  after_action :allow_iframe, only: :embed

  def embed
  end

private

  def allow_iframe
    response.headers.except! 'X-Frame-Options'
  end
end

Or, of course, you can code the after_action to set the value to something different:

class FacebookController < ApplicationController
  after_action :allow_facebook_iframe

private

  def allow_facebook_iframe
    response.headers['X-Frame-Options'] = 'ALLOW-FROM https://apps.facebook.com'
  end
end

Note that you need to clear your cache in certain browsers (Chrome for me) while debugging this.

Solution 2 - Ruby on-Rails

I just wanted to include an updated answer here for anyone who finds this link when trying to figure out how to allow your Rails app to be embedded in an I-Frame and running into issues.

As of writing this, May 28th 2020, the X-Frame-Options changes are probably not your best solution to your problem. The "ALLOW-FROM" option has been totally disallowed by all major browsers.

The modern solution is to implement a Content-Security-Policy and set a 'frame_ancestors' policy. The 'frame_ancestors' key designates what domains can embed your app as an iframe. Its currently supported by major browsers and overrides your X-Frame-Options. This will allow you to prevent Clickjacking (which the X-Frame-Options was originally intended to help with before it largely became deprecated) and lock down your app in a modern environment.

You can set up a Content-Security-Policy with Rails 5.2 in an initializer (example below), and for Rails < 5.2 you can use a gem like the Secure Headers gem: https://github.com/github/secure_headers

You can also override the policy specifications on a controller/action basis if you'd like.

Content-Security-Policies are great for advanced security protections. Check out all the things you can configure in the Rails docs: https://edgeguides.rubyonrails.org/security.html

A Rails 5.2 example for a Content-Security-Policy:

# config/initializers/content_security_policy.rb    
    Rails.application.config.content_security_policy do |policy|
      policy.frame_ancestors :self, 'some_website_that_embeds_your_app.com'
    end

An example of a controller specific change to a policy:

# Override policy inline
class PostsController < ApplicationController
  content_security_policy do |p|
    p.frame_ancestors :self, 'some_other_website_that_can_embed_posts.com'
  end
end

Solution 3 - Ruby on-Rails

The answers above really helped me, but in 2021 using a Rails 4.2 app I needed to turn off X-Frame-Options and specify a Content-Security-Policy for only a couple URLs.

Specifically I am using 2checkout as a payment provider and they open up some URLs in iframes....

This is how I did it

class HomeController < ApplicationController
    after_action :allow_2checkout_iframe, only: [:privacy, :terms_of_service]

    def privacy
    end

    def terms_of_service
    end

    private
        def allow_2checkout_iframe
            response.headers.except! 'X-Frame-Options'
            response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://secure.2checkout.com"
        end
end

Solution 4 - Ruby on-Rails

For Rails 5+, use response.set_header('X-Frame-Options', 'ALLOW-FROM https://apps.facebook.com') instead. Or if ALLOW-FROM doesn't work and you need a quick fix, you can set it to ALLOWALL

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChris PetersView Question on Stackoverflow
Solution 1 - Ruby on-RailsChris PetersView Answer on Stackoverflow
Solution 2 - Ruby on-Railsarmont_developmentView Answer on Stackoverflow
Solution 3 - Ruby on-RailsDagmarView Answer on Stackoverflow
Solution 4 - Ruby on-Railscamilo.foreroView Answer on Stackoverflow