Rails Responds with 404 on CORS Preflight Options Request

Viewed 26718

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?

8 Answers

Here's a Rails 5 solution with the gem rack-cors.

0) What are we trying to do?

Here's a super clear article on CORS (Cross-Origin Resource Sharing).

Your app needs to accept the preflighted requests, which are OPTIONS requests made with special headers like below. In this case the browser is checking the authorization for a POST request from https://some-site.com, with a given Request-Headers that could be anything.

Origin: https://some-site.com 
Access-Control-Request-Method: POST 
Access-Control-Request-Headers: x-csrf-ump-token

And respond with a simple text content (it doesn't matter) with appropriate headers, like below:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Headers: x-csrf-ump-token
Access-Control-Max-Age: 1728000

NB: Access-Control-Allow-Headers is optional. We'll set it to any below.

1) Handle the OPTIONS requests

# config/routes.rb
match '*all', to: 'cors#preflight_check', via: [:options]

# app/controllers/cors_controller.rb
class CorsController < ApplicationController
  def preflight_check
    render plain: 'OK!'
  end
end

2) Now add the proper headers with rack-cors

# config/initializers/cors.rb

# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      max_age: 1728000
  end
end

Check this article to further customize this config, for various origins.

Related