93

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?

Chris Peters
  • 17,918
  • 6
  • 49
  • 65

4 Answers4

148

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.

Chris Peters
  • 17,918
  • 6
  • 49
  • 65
  • How would you get this to work on a redirect_to? (Am trying right now with my Angular app and it is not working) – kittyminky Oct 29 '14 at 20:17
  • I'd assume that both the action containing the `redirect_to` and the action that it redirects to would need this to be applied. Are you getting a particular error? Sounds like a good new question on Stack Overflow! – Chris Peters Oct 30 '14 at 01:41
  • I realized I had the `after_action` *before* it was redirected to the final controller action that redirects to the `Angular` routes. Thank you! – kittyminky Oct 30 '14 at 19:06
  • It is not _required_ to do this in an `after_action`, though it is handy to do so e.g. in a `Frontend::BaseController` where it applies to the whole frontend. You may as well run `response.headers.except! ...` within an action. – codener May 18 '16 at 12:17
  • 2
    As of now, not working in Chrome. Console error is "Invalid 'X-Frame-Options' header encountered when loading 'child': 'ALLOW-FROM parent' is not a recognized directive. The header will be ignored." Marked as won't fix in Chromium, with an alternative: "'frame-ancestors' is shipping in both Chrome and Firefox, and is the right way to support this functionality." https://bugs.chromium.org/p/chromium/issues/detail?id=129139 – richardkmiller Jan 09 '18 at 18:11
21

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
  • Can also use a lambda for dynamic values: `p.frame_ancestors :self, -> { company&.allowed_domain || 'none' }` – Arctodus Oct 06 '20 at 16:50
  • I'm using `frame_ancestors` and it works in every browser, but Safari. Any insight? – Matt Dec 02 '20 at 15:23
  • @Matt - I believe Safari currently prevents 3rd party iframes from storing cookies - this is a major limitation of using iframes in Safari and may be the cause of your issue. As far as I know there are no good work-arounds. Check this stack overflow for more information: https://stackoverflow.com/questions/59723056/safari-13-iframe-blocks-cors-cookies – armont_development Dec 02 '20 at 20:03
  • Guys, what is the way to allow to be embedded for any domain using the 'content security policy'? https://stackoverflow.com/questions/71115047/rails-6-allow-the-specific-pages-be-embedded-in-iframe-by-any-other-domain – Ivan Feb 14 '22 at 21:12
4

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
Dagmar
  • 2,968
  • 23
  • 27
-2

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

camilo.forero
  • 520
  • 6
  • 10
  • 2
    Both these options (ALLOW-FROM and ALLOWALL) are not valid anymore: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options – ar31an Apr 30 '21 at 10:31