Duetcode

Adjust CORS settings

16 August 2020

In the previous section, we integrated Swagger UI into the bookmarker application. This week, we will adjust CORS settings to allow browser-based applications to make cross-domain requests. It will be a relatively small topic when compared with previous sections, but I didn’t want to bloat this part by mixing with other topics.

To better understand the CORS problem, we first need to check what is same-origin policy used by browsers.

What is the same-origin policy?

Essentially, same-origin is a web security mechanism that protect internet users. When you’re surfing on the web, modern browsers load the documents that only came from the same origin URL. With basic words, if you’re surfing on www.example-one.com, the browser doesn’t allow to load any document or scripts from www.example-two.com. Origin URL has three parts which are protocol (eg. http or https), host (www.example.com) and port (http:// is port 80 by default). If one of them is different, then the browser doesn’t load the document or script. You can see the example error when I try to send a request to https://google.com while I’m surfing on https://duetcode.io

CORS Error

Rails API-only application host

If we have a different host as an API application (let’s say API served with api.bookmarker.com meanwhile actual website served with www.bookmarker.com), the website is prevented by same-origin policy since we have different hostnames, and the web client can’t get the data from API endpoints.

What is CORS? (Cross-Origin Resource Sharing)

Suppose we want to load documents or scripts from different origin URLs, which means cross-origin, as we said in the previous example. In that case, we can adjust cross-origin resource sharing (CORS) settings to allow different origin URLs. For our example, if we add Access-Control-Allow-Origin: http://www.bookmarker.com to the response header of the API endpoints, browsers allow the web client to send a request.

How the web client API get a response header if they can’t load the documents? The browser first initiates OPTIONS request to the API and then got the response back to check if it has Access-Control-Allow-Origin header with appropriate cross-origin URL to send the actual request (GET, POST, etc.).

How we can adjust CORS for rails applications?

When you set up the application with an API flag, you get rack-cors gem in Gemfile but commented. We will uncomment this line and run bundle install command.

Gemfile
# frozen_string_literal: true

# ...

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS)
gem 'rack-cors', '~> 1.1'

# ...

After the installment, create an initializer file as config/initializers/cors.rb for the cors settings.

config/initializers/cors.rb
# frozen_string_literal: true

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:8080'
    resource '/api/v1/*',
             headers: :any,
             methods: %i[get post put patch delete options head]
  end
end

For the demonstration purpose, I will set http://localhost:8080 as a cross-origin URL. (You can imagine that we have a react application being served on 8080 port on the development environment.) Also, we set only the api/v1 namespace to accept cross-origin URLs.

With the CORS settings, keep in mind that Rails 6 blocks the requests coming from unknown hosts, so we also need to add the same URL to the hosts.

config/environments/development.rb
# frozen_string_literal: true

Rails.application.configure do
  # ...

  config.hosts << 'http://localhost:8080'
end

We adjust the CORS settings here only for the development environment, but keep in mind that you need to adjust for other environments like a sandbox, staging, production, etc.

Summary

In this chapter, we completed the CORS adjustments for the bookmarker application. You can find the source code of bookmarker application on github. You can also find the previous chapter here.

Thank you for reading! If you want to be notified when I publish a new chapter, you can follow me on twitter.