Ruby-on-rails – No ‘Access-Control-Allow-Origin’ header is present on the requested resource

amazon s3amazon-cloudfrontcorsgoogle-chromeruby-on-rails

My bootstrap glyphicons show on other browsers, but I get this error on google chrome:

Font from origin 'http://d37p52igaahgm9.cloudfront.net' has been
blocked from loading by Cross-Origin Resource Sharing policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://www.anthonygalli.com' is therefore not
allowed access.

The error persists despite trying:

application_controller.rb

before_action :set_cors

def set_cors
  headers['Access-Control-Allow-Origin'] = '*'
  headers['Access-Control-Request-Method'] = '*'
end

application.rb

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

config.action_dispatch.default_headers = {
    'Access-Control-Allow-Origin' => '*',
    'Access-Control-Request-Method' => '*'
}

CORS Configuration Editor

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://www.anthonygalli.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://anthonygalli.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

REFERENCES

Best Answer

You don't need to (shouldn't be) generating the headers in every response.

In your case, I would wager the asset request from your browser is being "preflighted" with an OPTIONS request, but the CDN passes on the request without Access-Control request headers. The CDN thus (correctly) receives no CORS response headers from your Rails app, so the browser doesn't even attempt the GET request, and fails with the Cross-Origin error.

"preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

Your CDN needs be set up to forward the correct request headers to your app server such that it knows to generate the CORS headers. Then, the CDN will pass these CORS response headers along to the browser.

When you want OPTIONS responses to be cached, configure CloudFront to forward the following headers: Origin, Access-Control-Request-Headers, and Access-Control-Request-Method.

http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors

If you make the change to your CDN for those headers and then invalidate your assets, your rack-cors configuration by itself should work just fine.

# config/initializers/cors.rb

# @note: must be run after initializers/_assets.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    # All asset requests should be to rails prefixed assets paths
    # serverd from the asset pipeline (e.g.: "/assets/*" by default)
    resource "#{Rails.application.config.assets.prefix}/*",
      # Allow any request headers to be sent in the asset request
      # https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers
      headers: :any,
      # All asset fetches should be via GET
      # Support OPTIONS for pre-flight requests
      # https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
      methods: [:get, :options]
  end
end