The Name's CORS. Rack::CORS.
Aidi Rivera
Posted on July 17, 2019
My latest project required using Rails to create an API backend. Ruby has a nifty --api
flag that creates a repo with only the folders you would need. It cuts down a lot of the extra folders you would get when normally creating a new Rails app. It also automatically adds a line into your gemfile to include Rack-CORS.
Following instructions, I uncommented rack-cors
in my gemfile to make sure it was "working" and copy-pasted some extra code into my application.rb
file "to allow CORS to work". Done. Cool.
As usual, I was absolutely baffled. What had I just done, what was anything actually doing, what did any of it mean and what even was the meaning of life?? I moved on and hoped I'd understand later on.
I never really got that explanation. So I decided to research it myself. I regretted it instantly.
What is CORS?
Anytime you want to bring resources into your web app that come from different origins, you'd implement Cross-Origin Resource Sharing, or CORS for short. CORS has standardized the way we retrieve cross-origin resources. How? …
What this means is that CORS is going ahead and telling your browser which resources your app is allowed to access when it's trying to access a resource that is not at its original domain (aka cross-origin).
And the only reason CORS even needs to do that is because of the Single-Origin Policy, which states that only resources from a single origin can be requested. Browsers abide by this policy by default for obvious security reasons. But that means we need to somehow figure out how to allow the cross-origin requests we do want to go through.
We do that by including the right CORS headers into our request.
Side note: JSONP (JSON with Padding) is another way to resolve the same-origin policy. Because HTML script tags are an exception to the cross-origin restrictions, JSONP simply turns data into an HTML script tag to bypass the single-origin policy. It's admittedly a bit “hacky” so not really used anymore unless a browser doesn't support CORS. *
What's Rack::CORS?
Rack::CORS is a middleware gem for Rack compatible web apps that helps you more easily implement CORS. It breaks the things down and turns making those cross-domain requests a cleaner and easier process. Plus, the error messages are super helpful.
How To Use It
The documentation for Rack CORS is helpful in explaining what to add where, but if you don't know much about CORS in general, it can be difficult to understand what you're looking at.
First, as the documentation explains, you want to open your config/application.rb
file and add something like this into your code:
config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'example.com', 'localhost:3000'
resource '/publicStuff/*', headers: :any, methods: [:get, :post]
resource '/myStuff/*', headers: :any, methods: :any
end
end
The first line uses
insert_before 0
so Rack CORS can run before any other middleware that might interfere with it.Line three is where you define which origins your app will accept resources from. This will be the domain name of the origin(s) you want to let through.
Line four is doing the same thing except we're defining the resource(s), or path(s), that we want to allow through.
In the same line you've defined your allowed resource path(s), you'll define which request parameters to allow. Most likely you'll be using the methods and headers parameters. If you want to allow multiple methods, you'd use the bracket notation to list your methods:
methods: [:get, :post, :options]
Let's look at the above example again and break things down.
config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'example.com', 'localhost:3000'
resource '/publicStuff/*', headers: :any, methods: [:get, :post]
resource '/myStuff/*', headers: :any, methods: :any
end
end
It allows requests from the
example.com
domain or fromlocalhost:3000
.-
The paths allowed are:
- any path that starts with
/publicStuff/
and - the
/myStuff/
path. - These possible paths could look something like
example.com/publicStuff/records/
orlocalhost:3000/myStuff/
.
- any path that starts with
For the
'/publicStuff/'
path it allows requests with any header, but only allows requests with the GET and POST method.Requests for the
'/myStuff/'
path are allowed with any method and any header.
If you're creating a small app you'll only ever run on your own computer or localhost, you might not need to worry about defining specific origins and routes:
You might have noticed the '*'
wildcard is a sort of 'all things go' symbol. Using the '*'
wildcard when defining the origin would allow all requests from any origin. Same would apply when using the wildcard for your resource paths. With resource paths, you can either allow any and all resource paths, (resource '*'
) or allow any extension of a specific path root (resource '/examplepath/* '
).
When defining parameters, you'll instead want to use something like headers: :any
and not the '*'
wildcard.
So if you were to exclusively use the '*'
wildcard for your small localhost app it might look something like this:
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: :any
end
end
That's nice and simple.
There's a lot more to CORS than what's written here, and could be a whole blog post, or two, on its own. So if the concept still feels a little shaky, that's ok! There's a lot going on there that you don't necessarily need to understand to be a good programmer, but if you can understand what these few lines are doing it'll make some of those fetch request errors make a lot more sense.
Happy Coding!
Same-origin policy - MDN Web Docs
Cross-Origin Resource Sharing (CORS) - MDN Web Docs
CORS & JSONP
Rack CORS Documentation
Posted on July 17, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.