Skip to content

Instantly share code, notes, and snippets.

@dhoelzgen
Last active October 7, 2021 16:19
Show Gist options
  • Save dhoelzgen/cd7126b8652229d32eb4 to your computer and use it in GitHub Desktop.
Save dhoelzgen/cd7126b8652229d32eb4 to your computer and use it in GitHub Desktop.
CORS in Rails 4 APIs
class API::V1::BaseController < ApplicationController
skip_before_filter :verify_authenticity_token
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
headers['Access-Control-Max-Age'] = "1728000"
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
end
Rails.application.routes.draw do
namespace :api, :defaults => {:format => :json} do
namespace :v1 do
controller :whatever, path: '/whatever' do
match 'post_action', via: [ :post, :options]
end
end
end
end
class API::V1::WhateverController < API::V1::BaseController
def upload
# Do complicated super secret stuff
render json: { success: true }
end
end
@robinschaaf
Copy link

Thanks so much for posting this.
Just a little note in case anyone has the problem, I was performing post using Angular's $http.post, and needed to add 'content-type' to the allowed headers on the preflight check, I guess since angular's post always automagically adds json as the content type. Otherwise this worked like a charm!

@sebastialonso
Copy link

Thanks for the gist!

I have an issue though. It seems that the OPTION request worked, because I get 200 OK from my Angular app. But that's all I get, I don't get any response with the data I need.

Any idea how to solve this?

@Braidio
Copy link

Braidio commented Apr 17, 2015

@sebastialonso Same here. Any luck with that?

@pasupulaphani
Copy link

@sebastialonso , Please have a look at line 22

render :text => ''

Change the above to send same response to all Options reqs. You can extend this by setting a necessary handler instead of render.

@jnwelzel
Copy link

jnwelzel commented May 4, 2015

So you're basically all-round disabling the same-origin policy?

@badnorseman
Copy link

I'm getting this error:

XMLHttpRequest cannot load http://localhost:3000/api/auth/google_oauth2. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.

Any suggestions?

@ekyfauzi
Copy link

I have same error like @urbanvikingr
I'm use Rails 4 for backend and Angular for the frontend

Please help :(

@ekyfauzi
Copy link

Hi, finally I solve the problem
the problem is in my Rails app

I found this:
https://github.com/cyu/rack-cors

@urbanvikingr I hope it can solve your problem too

@snappa
Copy link

snappa commented Sep 17, 2015

Wow, thank you so much! This helped me over the hurdle testing my ionic app. I know I have some tweaking to get this dialed in just right but this was immensely helpful!

@jmillman
Copy link

How can you do this with the assets pipeline?

@panchew
Copy link

panchew commented Oct 10, 2015

Thank you very much for posting this.
It worked perfectly for me.

@npetkov
Copy link

npetkov commented Feb 24, 2016

Just in case someone stumbles upon this gist, here's a more brief solution to the problem.

  1. Tell rails (rack) what to do with OPTIONS requests:

    Append this simple line to the end of your routes.rb file:

      match '*path', via: [:options], to:  lambda {|_| [204, {'Content-Type' => 'text/plain'}, []]}

    What does it do? It will handle OPTIONS requests on all of your routes, responding with no content and the appropriate Access-Control headers. The response is rendered on rack level, so you won't have to think about this on higher levels (e.g. controllers).

  2. Add the headers you need via rack-cors:

    You can insert this snippet in environments/development.rb.

    config.middleware.insert_before 0, 'Rack::Cors', logger: (-> { Rails.logger }) do
      allow do
        origins 'localhost:3000'
    
        resource '*',
          headers: :any,
          methods: [:get, :post, :delete, :put, :patch, :options, :head],
          max_age: 0
      end
    end

    That's it really.

@gabamnml
Copy link

Awesome! thanks.

@MartinKei
Copy link

@npetkov - thank you very much!

@nshoes
Copy link

nshoes commented Jul 20, 2016

@npetkov - 👍

@davidcotter
Copy link

@npetkov - thanks! For the first part I added this to routes and it got everything working:

 Rails.application.routes.draw do
   ...
   match '*path', via: [:options], to:  lambda {|_| [204, {'Access-Control-Allow-Headers' => "Origin, Content-Type, Accept, Authorization, Token", 'Access-Control-Allow-Origin' => "*", 'Content-Type' => 'text/plain'}, []]}
end

That solved the problem. What is the second part for

@cesc1989
Copy link

cesc1989 commented Dec 6, 2016

Thanks a lot @npetkov

And thank you @davidcotter too

@aymericbouzy
Copy link

I opened an issue for that rails/rails#27655

@wellington1993
Copy link

Thanks

@jarrettgreen
Copy link

I get a Rack lint error when trying to pass any headers with a 204.

@jpmagido
Copy link

Nice, you made my day ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment