Giter Site home page Giter Site logo

cyu / rack-cors Goto Github PK

View Code? Open in Web Editor NEW
3.3K 40.0 262.0 671 KB

Rack Middleware for handling Cross-Origin Resource Sharing (CORS), which makes cross-origin AJAX possible.

License: MIT License

Ruby 19.94% JavaScript 76.90% CSS 1.98% HTML 0.26% CoffeeScript 0.92%

rack-cors's Introduction

Rack CORS Middleware Build Status

Rack::Cors provides support for Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.

The CORS spec allows web applications to make cross domain AJAX calls without using workarounds such as JSONP. See further explanations on MDN

Installation

Install the gem:

gem install rack-cors

Or in your Gemfile:

gem 'rack-cors'

Configuration

Rails Configuration

For Rails, you'll need to add this middleware on application startup. A practical way to do this is with an initializer file. For example, the following will allow GET, POST, PATCH, or PUT requests from any origin on any resource:

# config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :patch, :put]
  end
end

NOTE: If you create application with --api option, configuration is automatically generated in config/initializers/cors.rb.

We use insert_before to make sure Rack::Cors runs at the beginning of the stack to make sure it isn't interfered with by other middleware (see Rack::Cache note in Common Gotchas section). Basic setup examples for Rails 5 & Rails 6 can be found in the examples/ directory.

See The Rails Guide to Rack for more details on rack middlewares or watch the railscast.

Read more about it here in the Rails Guides

Rack Configuration

NOTE: If you're running Rails, adding config/initializers/cors.rb should be enough. There is no need to update config.ru as well.

In config.ru, configure Rack::Cors by passing a block to the use command:

use Rack::Cors do
  allow do
    origins 'localhost:3000', '127.0.0.1:3000',
            /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
            # regular expressions can be used here

    resource '/file/list_all/', :headers => 'x-domain-token'
    resource '/file/at/*',
        methods: [:get, :post, :delete, :put, :patch, :options, :head],
        headers: 'x-domain-token',
        expose: ['Some-Custom-Response-Header'],
        max_age: 600
        # headers to expose
  end

  allow do
    origins '*'
    resource '/public/*', headers: :any, methods: :get

    # Only allow a request for a specific host
    resource '/api/v1/*',
        headers: :any,
        methods: :get,
        if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
  end
end

Configuration Reference

Middleware Options

  • debug (boolean): Enables debug logging and X-Rack-CORS HTTP headers for debugging.
  • logger (Object or Proc): Specify the logger to log to. If a proc is provided, it will be called when a logger is needed. This is helpful in cases where the logger is initialized after Rack::Cors is initially configured, like Rails.logger.

Origin

Origins can be specified as a string, a regular expression, or as '*' to allow all origins.

*SECURITY NOTE: Be careful when using regular expressions to not accidentally be too inclusive. For example, the expression /https:\/\/example\.com/ will match the domain example.com.randomdomainname.co.uk. It is recommended that any regular expression be enclosed with start & end string anchors, like \Ahttps:\/\/example\.com\z.

Additionally, origins can be specified dynamically via a block of the following form:

  origins { |source, env| true || false }

A Resource path can be specified as exact string match (/path/to/file.txt) or with a '*' wildcard (/all/files/in/*). A resource can take the following options:

  • methods (string or array or :any): The HTTP methods allowed for the resource.
  • headers (string or array or :any): The HTTP headers that will be allowed in the CORS resource request. Use :any to allow for any headers in the actual request.
  • expose (string or array): The HTTP headers in the resource response can be exposed to the client.
  • credentials (boolean, default: false): Sets the Access-Control-Allow-Credentials response header. Note: If a wildcard (*) origin is specified, this option cannot be set to true. Read this security article for more information.
  • max_age (number): Sets the Access-Control-Max-Age response header.
  • if (Proc): If the result of the proc is true, will process the request as a valid CORS request.
  • vary (string or array): A list of HTTP headers to add to the 'Vary' header.

Common Gotchas

Origin Matching

When specifying an origin, make sure that it does not have a trailing slash.

Testing Postman and/or cURL

  • Make sure you're passing in an Origin: header. That header is required to trigger a CORS response. Here's a good SO post about using cURL for testing CORS.
  • Make sure your origin does not have a trailing slash.

Positioning in the Middleware Stack

Positioning of Rack::Cors in the middleware stack is very important. In the Rails example above we put it above all other middleware which, in our experience, provides the most consistent results.

Here are some scenarios where incorrect positioning have created issues:

  • Serving static files. Insert before ActionDispatch::Static so that static files are served with the proper CORS headers. NOTE: this might not work in production as static files are usually served from the web server (Nginx, Apache) and not the Rails container.

  • Caching in the middleware. Insert before Rack::Cache so that the proper CORS headers are written and not cached ones.

  • Authentication via Warden Warden will return immediately if a resource that requires authentication is accessed without authentication. If Warden::Manageris in the stack before Rack::Cors, it will return without the correct CORS headers being applied, resulting in a failed CORS request.

You can run the following command to see what the middleware stack looks like:

bundle exec rails middleware

Note that the middleware stack is different in production. For example, the ActionDispatch::Static middleware will not be part of the stack if config.serve_static_assets = false. You can run this to see what your middleware stack looks like in production:

RAILS_ENV=production bundle exec rails middleware

Serving static files

If you trying to serve CORS headers on static assets (like CSS, JS, Font files), keep in mind that static files are usually served directly from web servers and never runs through the Rails container (including the middleware stack where Rack::Cors resides).

In Heroku, you can serve static assets through the Rails container by setting config.serve_static_assets = true in production.rb.

Custom Protocols (chrome-extension://, ionic://, etc.)

Prior to 2.0.0, http://, https://, and file:// are the only protocols supported in the origins list. If you wish to specify an origin that has a custom protocol (chrome-extension://, ionic://, etc.) simply exclude the protocol. See issue.

For example, instead of specifying chrome-extension://aomjjhallfgjeglblehebfpbcfeobpga specify aomjjhallfgjeglblehebfpbcfeobpga in origins.

As of 2.0.0 (currently in RC1), you can specify origins with a custom protocol.

Rails 6 Host Matching

Rails 6 will block requests from unauthorized hosts, and this issue can be confused as a CORS related error. So in development, if you're making requests using something other than localhost or 127.0.0.1, make sure the server host has been authorized. More info here

rack-cors's People

Contributors

adambutler avatar aguynamedben avatar aithscel avatar bfad avatar blt04 avatar borzik avatar boz avatar bradgessler avatar cyu avatar danallison avatar dependabot[bot] avatar dipspb avatar dmyers avatar dmytrostepaniuk avatar dwbutler avatar dyba avatar eoinkelly avatar everton avatar gabo-cs avatar hs-eallen avatar lambda2 avatar mxfive avatar oboxodo avatar ohbarye avatar olleolleolle avatar pointlessone avatar rossmeissl avatar rwd avatar teleological avatar trevoke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rack-cors's Issues

Debug output cannot be disabled

The gem seemingly always creates a logger. On a busy API server, the log quickly grew due to four lines of text each request.

Access-Control-Allow-Origin is returned only if Origin header is set

Rails 3.2.13, Ruby 2.0.0, Mongoid
config/application.rb:

...
config.middleware.use Rack::Cors do
      allow do
        origins '*'
        # location of your API
        resource '/api/*', :headers => :any, :methods => [:get, :post, :options, :put]
      end
    end

Using HTTPie:

http -h http://localhost:8080/api/organizations

Access-Control-Allow-Origin is NOT returned

If I set an Origin header:

http -h http://localhost:8080/api/organizations Origin:http://ohanapi.org

I get:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT
Access-Control-Allow-Origin: http://ohanapi.org

Is this expected? I was under the impression that Access-Control-Allow-Origin would be set to * even if no Origin was specified.

GitHub's API returns * when no Origin is specified. How can I get that behavior as well?

Allow lambda in origins

From looking at the allow_origin? in cors.rb, it looks like a lambda is allowed, but the origins setter method does not pass lambdas through.

Lambdas are useful for dynamically determining the eligibility of a domain.

Unsuccessful preflight passed through

If a preflight request does not pass the checks, because either Access-Control-Request-Method or Access-Control-Request-Headerscontains values that are not whitelisted, the request is passed up the rack stack. This will usually lead to an exception, because nothing in the stack handles the OPTIONS request.

I think the better behavior would be to return a 403 error.

I also noticed that on a successful preflight request the status code is 200, but I think 204 would be more appropriate.

rack-cors always prevents access when a browser gives "Origin: file://"

For file:// origins, rack-cors always returns Access-Control-Allow-Origin: null. AFAICT, this means that it always tells the browser that a request made with the header Origin: file:// is not permitted, even when you have configured file:// as an allowed origin.

This is a problem because at least some browsers (tested on UIWebView on iOS 7.1, Safari 7.1) send Origin: file://. I see that other browsers (Firefox, e.g.) send Origin: null for XHR requests from file:// pages. It seems like the solution to this would be to preserve the seen Origin header for this type of request and send the same thing back.

I'll put together a patch for this; please let me know if you see a problem with this approach.

Release new version?

Any chance you could release a new version to rubygems with the latest fixes added 6-7 months ago?

Session cookie is gone

Im using Rails 3.2.9 and I'm not sure what's wrong but it seems that there's a new session id whenever a cross-domain ajax request is made. For example, I am in domain.com and I make an ajax request to api.domain.com.

I'm using active_record_store and here is how it's configured:

MyApplication::Application.config.session_store :active_record_store, :key => 'asdf', :expire_after => 1.year, :domain => :all

I can see when I inspect it through the browser that the cookie's domain is set to '.domain.com' which should share the cookie across subdomains.

Here are some request headers which might help.

$ request.headers['rack.request.cookie_hash']
=> {}
$ request.headers['rack.session']
=> #<Rack::Session::Abstract::SessionHash:0x3fd108f480bc not yet loaded>
$ request.headers['rack.session.options']
=> {:path=>"/", :domain=>:all, :expire_after=>1 year, :secure=>false, :httponly=>true, :defer=>false, :renew=>false, :id=>nil}

I hope you can help me. Thanks a lot!

HTTP Token: Access denied In Production/Staging Only

Alright, I give up. I need some help on this.

I have Rack::Cors running fine in Development, but as soon as I deploy to another environment, it fails with "HTTP Token: Access denied".

I deployed to my staging env (which is identical to production) and I keep getting HTTP Token: Access denied.

Im at a loss here, iv got no idea how to fix it. Can anyone suggest anything?

My config looks like:

config.middleware.use Rack::Cors do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :delete, :options]
  end
end

documentation for multiple paths

if I want to cover both of these paths:

  • /assets/*
  • /assets/initializers/*

Do I need two calls to resource, one for each?, Or will one call to resource "/assets/* cover both?

XHR request using CORS always returns a 403 Forbidden when response content-type is application/json

To start, I don't think this is actually a bug in rack-cors, but the problem I'm having seems to be rather esoteric and possibly related to CORS, so this seemed like a good place to start.

The scenario is, I'm building an app with a Backbone.js frontend and a Sinatra api/backend. Initially they were all mounted inside the same rack app, but I wanted to break them out into separate apps, with the api at api.someurl.com and the frontend at someurl.com. So I started brushing up on CORS. I attempted to just set the appropriate headers myself in a before/after filter in Sinatra, but no matter what I did Chrome wouldn't allow the request. Eventually I found rack-cors, implemented it like so:

use Rack::Cors do
  allow do
    origins  "*"
    resource "*", headers: :any, methods: [:get, :post, :options]
  end
end

And suddenly Chrome started making the cross-domain request. I looked at the CORS headers that rack-cors generates and they were exactly the same ones I'd been setting manually, and yet, inexplicably they didn't work (when I set them manually). Out of curiosity, if you happen to have any idea why that might be, I'd love to know.

But, even though the request was now going through, it was actually failing with a 403 Forbidden. The request I was making was a really basic request; I just wanted to see the request/response headers and body in Chrome inspector. This was it:

<script>
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "http://api.someurl.com", true);
  xhr.setRequestHeader("Content-Type", "application/json");
  xhr.send();
</script>

And it was generating a 403 Forbidden. Interestingly, the response content-type was text/plain. Which is weird, because it's a pure JSON api, so I globally override the content-type on all responses to application/json in a before/after filter, like so:

before do
  response["Content-Type"] = "application/json"
end

I was pretty stumped, so I started changing the request/response content-types to see what would happen. If I remove the content-type override (which the response then defaults to text/html) or set it to any other content-type than application/json, it works. I get a 200 and the correct data back. The actual request content-type seems to be irrelevant. I changed the request to text/html, set the response to application/json, and I get a 403 Forbidden. But, if I view that same resource in the browser, which requests with text/html, then I get a 200 response, the JSON data, and the content-type is application/json.

So basically, there's something about making a CORS XHR request to a Sinatra/rack app using rack-cors, with the content-type for responses set to application/json, that causes the response to hork and return a 403 Forbidden.

Any help would be much appreciated. Thanks!

Duplicate Access-Control-Allow-Origin Headers being returned

When making a request to a CORS-enabled URL, rack-cors is adding both the Access-Control-Allow-Origin: * and Access-Control-Allow-Origin: http://calling-server.example.com/ headers. This causes browsers to fail due to there being two headers.

What is going wrong here? Is this a configuration issue?

DOC: using this middleware for assets on heroku

With Rails4 (maybe others too), heroku precompile the assets but, so… the middleware ActionDispatch::Static catch the request and respond very soon.

To define CORS for assets, don't use config.middleware.use Rack::Cors, instead use config.middleware.insert_before ActionDispatch::Static, Rack::Cors to ensure thar CORS are applied before responding as static.

A typical scenario is "fonts" served as assets from heroku (not S3 synched), with cloudfront (or not) in the middle, requested from firefox (cors required).

I think is only necessary to update the documentation with this particular case, to avoid a lot of tears and pain to other developers.

Origin port

Access to a standard URL over port 80 from a page served from a non-standard port causes access denied.

Origin 'http://localhost:63342' is therefore not allowed access.

No mention of port in the docs or source. Is this a rack-cors issue?

Better document use of the credentials flag

I ran into an odd problem today on a company app suite. TL;DR - you need to set credentials: false if you want multiple JS endpoints all hitting the same API endpoint to work.

Some background:

  • my company has a suite of internal applications that all interact via AJAX with a central API server. (Rails 3.2.14, rack-cors 0.2.8) Each JS app is on a separate subdomain (so foo-app.example.com, bar-app.example.com)
  • the API server had the following configuration for rack-cors:
  Rails.application.config.middleware.insert_before Warden::Manager, Rack::Cors do
    allow do
      origins '*'
      resource '*',
        headers: :any,
        methods: [:get, :post, :put, :delete, :options]
    end
  end
  • the observed behavior was that the first application visited in a tab would load successfully, but navigating to another in the suite relatively quickly afterwards (in the same tab) would fail with a CORS error in the browser.
  • some digging revealed that when the first app preflighted its requests, the Access-Control-Allow-Origin header was set to foo-app.example.com. This result was cached by the browser, causing the second app's requests to fail.
  • adding credentials: false to the resource call caused the Access-Control-Allow-Origin to be correctly sent as * and solved the problem.

The behavior is definitely correct by the W3C docs, but it's not at all obvious (short of either extreme familiarity with the spec or reading the source) that the credentials flag would have this effect.

I'm happy to help write up some docs for this, but I'm not sure where they should go. In the meantime, perhaps this issue will save somebody else some confusion.

Access-Control-Allow-Credentials is not included in response

When a resource is declared where credentials: true, the preflight response includes Access-Control-Allow-Credentials: true, but the actual response does not. Chrome (at least) will not accept this response when credentials have been sent and the response does not have Access-Control-Allow-Credentials: true.

Only allows full URL in origins for https

Should allow http... whether you think that's secure or not shouldn't matter. It's useful for development regardless, and the alternative is to use *, which is certainly less secure.

documentation on how to implement?

hi - i want to use this in a rails 3 application. I've installed the gem but am uncertain where within my application to put the config block ( use Rack::Cors do ... end)? Documentation on this would be very helpful. Thank you!

Documentation not good.

In the main readme.md : "Put your code in “config/application.rb” on your rails application. "

huh?

Allow different protocols, don't assume http

First, this is a great rack application, I was thrilled to find it!

A minor thing that I ran into though:

The app won't work with our origin content://com.company.app (used by trigger.io among others). I solved it using a regular expression /content\:\/\/com\.company\.mobile/ which gets special treatment.

My suggestions for improvements:

  1. If a protocol is specified, pass the entire origin string through.†
  2. When no protocol is specified, allow any protocol. This would require some work, separating the protocol from domain in the logic determining origin matches.

† Look for :// to determine if a protocol is included, the current code checks specifically for file://, https:// and http:// allow them through.

For reference, this is the code I'm using:

config.middleware.use Rack::Cors do
  allow do
    origins /content\:\/\/com\.company\.app/
    resource '*', :headers => :any, :methods => [:get, :post, :put, :patch, :delete, :options]
  end
end

Adding origins dynamically

Does anyone know if there's there a way to dynamically add origins to the allowed list without having to restart the server?

Thanks

Does not seem to allow any URL's after installation

I had this ->

    # use this middleware gem to cause the API to be CORS capable.
    config.middleware.insert_before(Rack::ConditionalGet, Rack::Cors)  do

      allow do
        origins '*'
        resource '*',
                 :headers => :any,
                 :methods => [:get, :post, :options]
      end
    end

Then I tried this..

    config.middleware.use Rack::Cors do
      allow do
        origins '*'
        resource '*', :headers => :any, :methods => [:get, :post, :options]
      end
    end

POSTMAN plugin for Chrome seems to properly work, but nothing with the actual web browser itself.

Compatible with Rails 4.1?

I just did a simple modification to allow from all origins in my application.rb file:

config.middleware.insert_before ActionDispatch::Static, Rack::Cors do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :options]
  end
end

Now if I print my headers hash in the console in development mode all I see is this:

{"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff"}

This is reproducable in my production environment as well, also I tried config.middleware.use as demonstrated in the readme instead, but met similar results.

I'm still learning this CORS stuff, so perhaps I'm misunderstanding the use of this tool?

Here's the results of curl -I for localhost:3000:

curl -I localhost:3000
HTTP/1.1 200 OK
Date: Wed, 18 Jun 2014 17:17:39 GMT
Status: 200 OK
Connection: close
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
ETag: "0ebfe87b1643d171886e205392e3fb7b"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: request_method=HEAD; path=/
Set-Cookie: _session_id=9a50775731213939b7344e586c3e961e; path=/; HttpOnly
X-Request-Id: a9b1cfcc-1241-4163-920d-3edfd08ec81a
X-Runtime: 0.690068

OPTIONS requests succeed preflight, fail actual

I have spent a significant amount of time trying to resolve a situation where for an OPTIONS request, the preflight looks perfect but the actual request is considered an error by the browser. It turns out that the reason is that there is no Access-Control-Allow-Origin: * header in the actual request response. When I added it to the response, things work great!

Consider the current behavior:

  if env['HTTP_ORIGIN']
    if env['REQUEST_METHOD'] == 'OPTIONS'
      if headers = process_preflight(env)
        return [200, headers, []]
      end
    else
      cors_headers = process_cors(env)
    end
  end

The REQUEST_METHOD is "OPTIONS" on the actual request, but is not a preflight. Therefore, no cors_headers will be present. As I understand it, the Resource Processing Model indicates that it would be correct to "add a single Access-Control-Allow-Origin header, with either the value of the Origin header or the string "*" as value" as step three of any of "Simple Cross-Origin Request, Actual Request, and Redirects" server response.

I would like to fix this immediately if @cyu agrees with this analysis. If there is disagreement, I would like some interaction about why I cannot make an OPTIONS request. I believe this is related to #24.

Cannot perform update(PUT) action

I'm using Rails API(REST) with rack-cors. I can get record from server, but can't update.

Server Log:

Started OPTIONS "/sections?_dc=1394560533889&page=1&start=0&limit=25" for 127.0.0.1 at 2014-03-12 00:55:33 +0700

Started GET "/sections?_dc=1394560533889&page=1&start=0&limit=25" for 127.0.0.1 at 2014-03-12 00:55:34 +0700
Processing by SectionsController#index as /
Parameters: {"_dc"=>"1394560533889", "page"=>"1", "start"=>"0", "limit"=>"25"}
Section Load (0.5ms) SELECT A.*,B.name AS Page FROM sections AS A LEFT JOIN pages AS B ON A.page_id=B.id
Completed 200 OK in 191ms (Views: 5.9ms | ActiveRecord: 5.0ms)

Started OPTIONS "/sections/3?_dc=1394560545888" for 127.0.0.1 at 2014-03-12 00:55:45 +0700

ActionController::RoutingError (No route matches [OPTIONS] "/sections/3"):
actionpack (4.0.0) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call

Any idea will be appreciate

Documentation for Rails won't work in production environments

The rack middleware stack is different in production and does not actually contain ActionDispatch::Static, making the app fail to load with an error like the following:

No such middleware to insert before: "ActionDispatch::Static"

Changing the middleware to insert before to Rack::Runtime does work in production, like so:

module YourApp
  class Application < Rails::Application

    # ...

    config.middleware.insert_before "Rack::Runtime", "Rack::Cors" do
      allow do
        origins '*'
        resource '*', :headers => :any, :methods => [:get, :post, :options]
      end
    end

  end
end

Question about preflight request behavior.

Hi,

I'm a little confused about the expected behavior of the preflight request. If REQUEST_METHOD is OPTIONS and you also have Access-Control-Request-Method set to something else, like GET, the HTTP status code in the response is 200 instead of 204.

I was under the impression that any OPTIONS request would always return a 204 as long as I have an appropriate route defined, as explained here.

If I don't include the Access-Control-Request-Method header, then I get a 204 as expected, and I see this in the Rails log:

curl -i http://lvh.me:8080/api/locations -H "Origin: http://example.com" -X OPTIONS

Started OPTIONS "/api/locations" for 127.0.0.1 at 2014-10-14 15:09:23 -0400
Processing by Api::V1::CorsController#render_204 as JSON
  Parameters: {"unmatched_route"=>"locations"}
Can't verify CSRF token authenticity
Completed 204 No Content in 1ms (ActiveRecord: 0.0ms)

If I do include the Access-Control-Request-Method header and set it to something other than OPTIONS, then I get a 200 and I see this in the Rails log:

curl -i http://lvh.me:8080/api/locations -H "Origin: http://example.com" -H "Access-Control-Request-Method: GET" -X OPTIONS

Started OPTIONS "/api/locations" for 127.0.0.1 at 2014-10-14 15:14:44 -0400

This doesn't look right to me. Am I doing something wrong?

The GitHub API, for example, always returns a 204 if the request method is OPTIONS:

curl -i https://api.github.com/users/monfresh -H "Origin: http://travis-ci.org" -H "Access-Control-Request-Headers: Content-Type" -H "Access-Control-Request-Method: GET" -X OPTIONS

Also, I'm not sure if this standard behavior, but the GitHub API always returns the Access-Control-Allow-Headers even if the request does not include Access-Control-Request-Headers or Access-Control-Request-Method.

Expose only a subdomain?

I undertand that resource accepts a path, but how do I have CORS for only my api? api.example.com

config.middleware.insert_before 0, "Rack::Cors" do
  allow do
      origins '*'
      resource 'api.example.com/*', :headers => :any, :methods => [:get, :post, :options]
   end
end

How to add cors before cache

When I add

insert_after 0, Rack::Cors, logger: Rails.logger do ...

in application.rb

and enable Rack::Cache in production.rb

config.action_dispatch.rack_cache = true

Rack::Cache always ends up first in the middleware stack, and Cors second. If I understand the README correct Cors must be loaded before right? How do I do that?

EDIT: insert_before is what I need. dahhh :) You can delete this. Thx

Can't get rack-cors to work in production environment using nginx/unicorn.

It works in development, but setting the same in production or staging environments, rack-cors doesn't seem to add the appropriate headers. The preflight stuff looks okay.

Is there a specific problem with nginx configuration that causes the middleware to not work?

Here's my nginx config file:

# this can be any application server, not just Unicorn/Rainbows!
upstream mysite {
  # fail_timeout=0 means we always retry an upstream even if it failed
  # to return a good HTTP response (in case the Unicorn master nukes a
  # single worker for timing out).

  # for UNIX domain socket setups (configured in unicorn.rb)
  server unix:/tmp/.sock fail_timeout=0;

  # for TCP setups, point these to your backend servers
  # server 192.168.0.7:8080 fail_timeout=0;
  # server 192.168.0.8:8080 fail_timeout=0;
  # server 192.168.0.9:8080 fail_timeout=0;
}

server {

  client_max_body_size 4G;
  listen 443;
  server_name  mysite.com www.mysite.com;
  root /var/www/mysite/current/public;

  keepalive_timeout 5;
  try_files $uri/index.html $uri.html $uri @app;
  location @app {
    # an HTTP header important enough to have its own Wikipedia entry:
    # http://en.wikipedia.org/wiki/X-Forwarded-For
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # enable this if you forward HTTPS traffic to unicorn,
    # this helps Rack set the proper URL scheme for doing redirects:
    proxy_set_header X-Forwarded-Proto $scheme;

    # pass the Host: header from the client right along so redirects
    # can be set properly within the Rack application
    proxy_set_header Host $http_host;

    # we don't want nginx trying to do something clever with
    # redirects, we set the Host: header above already.
    proxy_redirect off;

    # set "proxy_buffering off" *only* for Rainbows! when doing
    # Comet/long-poll/streaming.  It's also safe to set if you're using
    # only serving fast clients with Unicorn + nginx, but not slow
    # clients.  You normally want nginx to buffer responses to slow
    # clients, even with Rails 3.1 streaming because otherwise a slow
    # client can become a bottleneck of Unicorn.
    #
    # The Rack application may also set "X-Accel-Buffering (yes|no)"
    # in the response headers do disable/enable buffering on a
    # per-response basis.
    # proxy_buffering off;

    proxy_pass http://mysite;
  }

  # Cross domain webfont access
  location ~* \.(?:ttf|ttc|otf|eot|woff|font.css)$ {
    add_header "Access-Control-Allow-Origin" "mysite.com";
    add_header "Access-Control-Allow-Origin" "www.mysite.com";

    # Also, set cache rules for webfonts.
    # See http://wiki.nginx.org/HttpCoreModule#location
    # And https://github.com/h5bp/server-configs/issues/85
    # And https://github.com/h5bp/server-configs/issues/86
    expires 1M;
    access_log off;
    add_header Cache-Control "public";
  }

  ssl on;
  ssl_certificate /home/ubuntu/ssl/mysite.com.crt;
  ssl_certificate_key /home/ubuntu/ssl/mysite.com.key;

}

ERROR NoMethodError: undefined method `debug'

I'm running the example config in a Rails 4 app with version 0.2.9 with the following:

config.middleware.insert_before 0, "Rack::Cors", :debug => true, :logger => (-> { Rails.logger }) do
    allow do
      origins '*'

      resource "/intents*",
        :headers => :any,
        :methods => [:get, :post, :options, :head],
        :max_age => 0

      resource "/widgets",
        :headers => :any,
        :methods => [:get, :head],
        :max_age => 0
    end
end

However when I run this I get:

ERROR NoMethodError: undefined method `debug' for #<Proc:0x007f5da0b2cbe0@/app/config/application.rb:79 (lambda)>

The trace comes from:

/app/vendor/bundle/ruby/2.1.0/gems/rack-cors-0.2.9/lib/rack/cors.rb:35:in `call'

Anyone else seeing this?

Too many js files being cached

A client accesses my app utilizing rack-cors from a random location. They send an ajax request which returns the appropriate information as html.

The issue I'm having is that every request a client sends, the client gets sent an additional js file with the response. This is because of the use of nonces (which I assume rack-cors is doing because there is no mention of it in rails documentation). How can I disable this? The below picture is if the client sends the request 3 times. 3 js files are sent, does anyone know of a fix so that the js is sent without the nonce, making it only request it once?

image

@cyu any thoughts?

attempt to do a HEAD request yields 403 forbidden

I've included the following to rack cors:

resource '*', headers: :any, methods: [:get, :head, :options]

Cross domain ajax requests work for GET but not for HEAD.

I would not know if it is browser related or back end related.

Rails 4.2 compatibility

Got the following deprecation warnings and errors after upgrading to rails 4.2.0.

DEPRECATION WARNING: The configuration option `config.serve_static_assets` has been renamed to `config.serve_static_files` to clarify its role (it merely enables serving everything in the `public` folder and is unrelated to the asset pipeline). The `serve_static_assets` alias will be removed in Rails 5.0. Please migrate your configuration files accordingly. (called from block in tsort_each at /usr/local/var/rbenv/versions/2.1.1/lib/ruby/2.1.0/tsort.rb:226)
/usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:232:in `quote': no implicit conversion of nil into String (TypeError)
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:232:in `block in origins'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:226:in `collect'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:226:in `origins'
    from /Users/code/app/config/application.rb:37:in `block (2 levels) in <class:Application>'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:41:in `instance_eval'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:41:in `allow'
    from /Users/code/app/config/application.rb:36:in `block in <class:Application>'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:26:in `instance_eval'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/rack-cors-0.3.1/lib/rack/cors.rb:26:in `initialize'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/stack.rb:43:in `new'
    from /usr/local/var/rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/actionpack-4.2.0/lib/action_dispatch/middleware/stack.rb:43:in `build'
...

Access-Control-Allow-Headers not being returned

Hi; I have a simple Rack app (using the Crepe framework) and I have Rack::Cors set up like this:

use Rack::Cors do
  allow do
    origins '*'
    resource '*', {
      headers: %w[Authorization Content-Type],
      methods: %w[HEAD OPTIONS GET POST PUT PATCH]
    }
  end
end

I then have a simple health check endpoint:

class API < Crepe::API
  get(:health) { head :no_content }
end

run API

And when I make an OPTIONS request…

% curl --include -H 'Origin: *' -X GET localhost:9292/health
HTTP/1.1 204 No Content
Cache-Control: max-age=0, private, must-revalidate
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: HEAD, OPTIONS, GET, POST, PUT, PATCH
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
Vary: Origin

I would expect to also see Access-Control-Allow-Headers: Authorization, Content-Type but that header never gets set. Am I doing anything wrong with setup?

Specify entire subdomain

Is there a way to specify that an entire subdomain (let's say api.example.com) should support CORS?

In particular, I'm looking to get this working with a Grape app mounted inside of Padrino...

Unicorn timeouts on many requests

During testing, discovered that Unicorn would time out (15s timeout) when presented with a lot of CORS requests using this gem. Calling with jQuery from a different machine on the local network on a local server, when doing a loop of even 10 async requests, Unicorn would stall, then restart the workers, and receive 10 OPTIONS and 10 GET requests. When calling manually at a lower rate, everything is fine.

At the same time, the app can handle thousands of non-CORS requests via curl without a hitch. Any ideas what can be going wrong? How high of a load can this middleware handle? I've implemented it with only the steps described in the readme (without explicit routes for OPTIONS, it works) by adding this to application.rb:

    config.middleware.insert_before 0, "Rack::Cors", :debug => true, :logger => (-> { Rails.logger }) do
      allow do
        origins 'http://localhost:3000'

        resource '*',
          :headers => :any,
          :methods => [:get, :post, :delete, :put, :options],
          :max_age => 0,
          :credentials => true
      end
    end

The request call looks like this:

      $.ajax({
        url: 'http://localhost:7000/user/',
        method: 'GET',
        dataType: 'json',
        headers: {
          'Accept': 'application/vnd.myapp.v1',
          'Authorization': 'w2z_UseVMZbtrzqogj1n'
        },
        success: function(e) {
          console.log(e);
        }
      })

Has anyone had a similar problem?

UPDATE:
Unicorn actually has trouble responding even to one request. Often it would take a few seconds until anything is even seen in the log, but sometimes it would even exceed 15 seconds. Disabling concurrency didn't solve the problem.

With Puma, everything is fine and snappy so far. I will actually use Puma in production so it seems fine for me, but leaving this here in case someone runs into problems with Rack::Cors and Unicorn.

uninitialized constant Rack::Cors

I'm having this issue when running a rails 3.2.12 app, it is running with unicorn on production environment

Here is the unicorn log.
/home/deploy/applications/wecul-api/releases/20130606154224/config/application.rb:72:in `<class:Application>'
:
uninitialized constant Rack::Cors
 (
NameError
)
    from /home/deploy/applications/wecul-api/releases/20130606154224/config/application.rb:12:in `<module:PeepoltvApi>'
    from /home/deploy/applications/wecul-api/releases/20130606154224/config/application.rb:11:in `<top (required)>'
    from /home/deploy/applications/wecul-api/releases/20130606154224/config/environment.rb:2:in `require'
    from /home/deploy/applications/wecul-api/releases/20130606154224/config/environment.rb:2:in `<top (required)>'
    from config.ru:4:in `require'
    from config.ru:4:in `block in <main>'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/builder.rb:51:in `instance_eval'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/builder.rb:51:in `initialize'
    from config.ru:1:in `new'
    from config.ru:1:in `<main>'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn.rb:44:in `eval'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn.rb:44:in `block in builder'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:722:in `call'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:722:in `build_app!'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/lib/unicorn/http_server.rb:140:in `start'
    from /home/deploy/applications/wecul-api/shared/bundle/ruby/1.9.1/gems/unicorn-4.6.2/bin/unicorn:126:in `<top (required)>'
    from bin/unicorn:16:in `load'
    from bin/unicorn:16:in `<main>'
E, [2013-06-06T21:20:28.718097 #7506] ERROR -- : reaped #<Process::Status: pid 30554 exit 1> exec()-ed
In my Gemfile I've added
gem 'rack-cors', :require => 'rack/cors'
In my config/application.rb I've added
config.middleware.use Rack::Cors do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :delete, :put, :options]
  end
end

I'd already tryied

  • Adding require 'rack/cors' in my `config/application.rb'
  • I've tryied this in the rails console with this output
Loading production environment (Rails 3.2.12)
irb(main):001:0> Rack::Cors
 => Rack::Cors

rails-api & rails 4 not working with rack-cors?

I've added rack-cors to my gemfile and put in my development.rb and production.rb (I also tried application.rb):

  config.middleware.insert_before ActionDispatch::Static, Rack::Cors do
    allow do
      origins '*.example.com'
      resource '*', :headers => :any, :methods => [:get, :post, :options]
    end
  end

When I run rake middleware, I can see Rack::Cors. When I do a curl request for development or production no CORS headers are added to the requests. There's no errors to try to track, so I'm at a loss for why this isn't working.

Can this be used to serve static assets?

Firefox and IE refuse to load webfonts if they're not loaded from the same domain the request came from. I'm attempting to use this gem to set the proper headers so they will accept the files.

Can this gem set access origin headers for assets in the /public directory of a Rails app?

rack-cors does not respond to OPTIONS request

I'm having some issues with Chrome sending an OPTIONS request to check the CORS settings. From what I can gather, rack-cors should properly reply to these requests, but this isn't the case for me.

I've set up rack-cors as middleware in Rails 3.1 and catch the OPTIONS requests to go to my CorsController. In there, I simply return head :200, and I assumed rack-cors would attach the needed headers. This is not the case however.

I can get thing to work if I manually send back the headers, using:

class Api::CorsController < ::ApplicationController
  respond_to :json, :xml

  def new
    headers['Access-Control-Allow-Origin'] = request.env['HTTP_ORIGIN']
    headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
    headers['Access-Control-Max-Age'] = '1000'
    headers['Access-Control-Allow-Headers'] = '*,x-requested-with,X-CSRF-Token'

    head :ok
  end
end

But I'd rather have rack-cors handle this to keep everything nicely together.

Any thoughts on why this isn't working? Also, I am not sure how I can set rack-cors to use the Rails logger instead of STDOUT? I know you use @logger = opts[:logger] and I assume I can set that to Rails.logger, but how would I set this option using this code?

    config.middleware.insert_before Warden::Manager, Rack::Cors do
      allow do
        origins %r{^https?:\/\/[a-z0-9\-]+.#{CONFIG['website_host']}:?\d*$}i
        resource '*',
          headers: ['Origin', 'Accept', 'Content-Type'],
          methods: [:get, :put, :create, :delete]
      end
    end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.