Rails Responds with 404 on CORS Preflight Options Request

26,355

Solution 1

Here's a solution with the rack-cors gem, which you said you tried. As others have mentioned, you didn't give much detail in regards to which front-end framework you're using and what the actual request looks like. So the following may not apply to you, but I hope it helps someone.

In my case, the gem worked fine until I used PUT (or PATCH or DELETE).

If you look in your browser developer console, look at the request headers, and you should have a line like this:

Access-Control-Request-Method: PUT

The important thing to note is that the methods you pass to resource are for the Access-Control-Request-Method, not the Request Method that is to come after the pre-flight check.

Note how I have :methods => [:get, :post, :options, :delete, :put, :patch] that will include all the methods I care about.

Thus your entire config section should look something like this, for development.rb:

# This handles cross-origin resource sharing.
# See: https://github.com/cyu/rack-cors
config.middleware.insert_before 0, "Rack::Cors" do
  allow do
    # In development, we don't care about the origin.
    origins '*'
    # Reminder: On the following line, the 'methods' refer to the 'Access-
    # Control-Request-Method', not the normal Request Method.
    resource '*', :headers => :any, :methods => [:get, :post, :options, :delete, :put, :patch], credentials: true
  end
end

Solution 2

Working on Rails 3.2.11.

I put

match '*path', :controller => 'application', :action => 'handle_options_request', :constraints => {:method => 'OPTIONS'}

in my routes.rb file. The key was to put it as top priority (on top of the routes.rb file). Created that action so that it is publicly available:

  def handle_options_request
    head(:ok) if request.request_method == "OPTIONS"
  end

And a filter in application controller:

 after_filter :set_access_control_headers

  def set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
  end

Solution 3

Yes, as others have pointed out there is a GEM to maybe do this better. But since I very much liked the method pointed out in the original blog post with the cors code, I've found the Rails 4 solution if you're using that code.

In your routes.rb:

match '*all' => 'my_method_name#cor', via: :options

In your my_method_name controller:

def cor
    # blank section for CORR
    render :text => ''
end

As long as you have that plus your other code:

before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
...

Then you should be set for Rails 4.

Solution 4

Perhaps this gist can help you: CORS in Rails 4 APIs

It adds the OPTIONS method to the route definition, and adds a filter to the API base controller which directly responds to OPTIONS requests with the correct header, and sets the correct CORS headers for all other actions, too.

Solution 5

I ran into the same issue, and am currently evaluating the following routes for any possible security / performance issues. They solve the issue, but...

match '/', via: [:options], 
 to:  lambda {|env| [200, {'Content-Type' => 'text/plain'}, ["OK\n"]]}
match '*unmatched', via: [:options],  
 to:  lambda {|env| [200, {'Content-Type' => 'text/plain'}, ["OK\n"]]}

Despite 'match' supposedly not working in Rails 4, apparently it does work if you restrict it to a specific method.

Share:
26,355

Related videos on Youtube

Rolandus
Author by

Rolandus

Updated on July 09, 2022

Comments

  • Rolandus
    Rolandus almost 2 years

    I'm creating a set of services using Rails 4, which I am consuming with a JavaScript browser application. Cross-origin GETS are working fine, but my POSTs are failing the preflight OPTIONS check with a 404 error. At least, I think that's what's happening. Here are the errors as they appear in the console. This is Chrome 31.0.1650.63 on a Mac.

    OPTIONS http://localhost:3000/confessor_requests 404 (Not Found) jquery-1.10.2.js:8706
    OPTIONS http://localhost:3000/confessor_requests No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. jquery-1.10.2.js:8706
    XMLHttpRequest cannot load http://localhost:3000/confessor_requests. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. main.html:1
    

    I've searched high and low for instructions on enabling CORS, and I'm stumped. The usual recommendation seems to be to put something like this in the Application controller, which I did.

    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, PUT, GET, OPTIONS'
      headers['Access-Control-Allow-Headers'] = '*'
      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, PUT, GET, OPTIONS'
        headers['Access-Control-Allow-Headers'] = '*'
        headers['Access-Control-Max-Age'] = '1728000'
        render :text => '', :content_type => 'text/plain'
      end
    end
    

    Followed by some kind of route in routes.rb that will redirect to this action when an OPTIONS request comes in.

    match "/*all" => "application#cors_preflight_check", :constraints => { :method => "OPTIONS" }
    

    The 'match' directive no longer works in Rails 4, so I fiddled with it, attempting to make it match POSTS directly, like this:

    post "/*all" => "application#cors_preflight_check", :constraints => { :method => :options }
    

    But it still doesn't work. Since the GET requests are working, I'm assuming that what I'm missing is the correct route for the OPTIONS request. However, I've tried every route I can think of, and nothing seems to let the request through.

    I also tried installing cyu/rack-cors, and this gives the same result.

    Anyone know what I'm doing wrong?

    • Matt Jones
      Matt Jones over 10 years
      match should still work, but it complains if you don't pass a via option. Doing it with post is definitely not going to work. As somebody who made this exact mistake before with rack-cors, I'm also going to ask: did you restart the server after configuring the rack-cors middleware? I spent an hour+ chasing that a while back myself. :)
    • collimarco
      collimarco over 8 years
      Same here. I've tried both the custom methods and the gem: everything works fine until an ActiveRecord::RecordNotFound exception is raised. In that case CORS doesn't work.
    • collimarco
      collimarco over 8 years
      I have further investigated the issue: in my case the preflight response headers are correct, while the actual request (when an exception is raised) produces a response without the CORS headers
  • dmur
    dmur almost 10 years
    This answer is for Rails 3, but the question is specifically about Rails 4.
  • mpowered
    mpowered over 9 years
    I'm not sure implementing a proxy server is really an answer to this. What if someone else wants to interact with your API? Don't you think Rails should respond properly to the options request?
  • po3t
    po3t over 9 years
    @AdamRobertson - You're probably right. My solution is not for publicly consumed APIs if anything other than GET requests are required. This solution should only be used for private APIs. That said, I was running into this problem regardless of what I did on the Rails end. No matter what kind of CORS solution I implemented, I would always make an OPTIONS request if I made anything other than a GET request. Chalk it up to lack of experience with Rails on my end.
  • Sebastialonso
    Sebastialonso over 9 years
    @ancajic Thanks for your answer. Is there any security implication with this that you know of?
  • AndroC
    AndroC over 9 years
    @Sebastialonso... Obviously it can cause problems if your HTTP API has endpoints that respond to requests with method=OPTIONS. Other than that, I don't see any security implications that would not occur already by enabling CORS. More experienced Rails developers might be of more help ;)
  • Yahor Zhylinski
    Yahor Zhylinski over 8 years
    Thanks. You saved my day:)
  • rramsden
    rramsden almost 8 years
    Nice solution, instead of setting the headers manually in the application controller I set them per environment. For development in config/environments/development.rb you can configure config.action_dispatch.default_headers = {} to include the COR headers.
  • Hendrik
    Hendrik over 7 years
    Not 100% sure where I had copied my rules from, some other SO post. But in this case adding the put made things work again.
  • Dorian
    Dorian over 7 years
    Same, with js fetch(), method: "PATCH" didn't work but method: "PUT" worked (both used the Access-Control-Request-Method)
  • Stéphane Bruckert
    Stéphane Bruckert almost 7 years
    :patch was missing from the CORS allowed methods! thanks
  • Ken Ratanachai S.
    Ken Ratanachai S. over 5 years
    Thanks!. credentials: true Saved my day!
  • Bhawan
    Bhawan over 4 years
    Don't we need to change anything in routes.rb file?
  • akostadinov
    akostadinov almost 3 years