Cross Origin Resource Sharing (CORS) Blocked for Cloudfront in Rails

Using a CDN like AWS Cloudfront helps speed up delivery of static assets to your visitors and reduce the load on your servers. Setting up Cloudfront for your Rails apps is very simple, thanks to gems like asset_sync that work nicely with Rails’ asset pipeline compilation process and S3.

One issue can however be tricky to solve sometimes: CORS blocks.

What is happening?

If your assets are served by a CDN like Cloudfront, they can be served from a domain like sdf73n7ssa.cloudfront.net while your app is served on www.myawesomeapp.com. This triggers CORS blocking in browsers to stop malicious websites fetching nasty resources while browsing a seemingly nice website.

The most common type of this issue is with fonts when you get something like this:

Font from origin 'https://sdf73n7ssa.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 'https://www.myawesomeapp.com' is therefore not allowed access.  

How to fix it?

When it comes to AWS Cloudfront, the most commonly suggested method is to allow CORS origins on the Cloudfront side. This method involves writing XML configuration code for AWS and uploading it on the S3 side.

There is a better and easier way!

I personally prefer using my DNS subdomains to solve this problem. If my CDN is behind cdn.myawesomeapp.com instead of sdf73n7ssa.cloudfront.net then browsers are not going to freakout and block them as cross domain security problems.

To point your subdomain to your AWS Cloudfront domain go to AWS Cloudfront control panel, select your Cloudfront distribution and enter your CDN subdomain into the Alternate Domain Names (CNAMEs) field. Something like cdn.myawesomeapp.com will do.

Now you can go to your DNS provider (like AWS Route 53) and create a CNAME for cdn.myawesomeapp.com pointing to sdf73n7ssa.cloudfront.net.

You can test your new CDN domain by making sure assets served from it are coming from Cloudfront.

curl -I http://cdn.myawesomeapp.com/assets/image.png  

should return something like this

HTTP/1.1 200 OK  
Content-Type: image/png  
Content-Length: 10414  
Connection: keep-alive  
Date: Mon, 22 Sep 2014 10:06:41 GMT  
Last-Modified: Sun, 06 Jan 2013 16:37:19 GMT  
ETag: "1c4bef3752c306b9c14a05b4a19d7d79"  
Accept-Ranges: bytes  
Server: AmazonS3  
Age: 1599  
X-Cache: Hit from cloudfront  
Via: 1.1 a3c44e1caa58818cd22903047dc0faf4.cloudfront.net (CloudFront)  
X-Amz-Cf-Id: sEbH-vV6deQra_YQa144RxtwhuJaWSrq-tpdiFxWdUbDbR2DnhoIrQ==  

But what about SSL?

This method works for non-SSL traffic, but in most cases we use a schema-less asset URL like //cdn.myawesomeapp.com for our resources so both http://cdn.myawesomeapp.com and https://cdn.myawesomeapp.com work. Using custom domain Cloudfront CDN names will break the SSL (https) version. You can use dedicated SSL enabled CDN from Cloudfront, but that’s usually very expensive.

Luckily, AWS supports SSL Server Name Indication (SNI).

To use it you need to upload your SSL certificate to AWS first. Unfortunately there is no UI for this on the AWS side yet, so you will need to install the AWS command line tool first (that’s easy). Once you have your AWS command line tool installed and configured with the AWS keys, you can upload your SSL certificate:

aws iam upload-server-certificate --server-certificate-name my_certificate_name --certificate-body file://my_cert.crt --private-key file://my_key.key --certificate-chain file://intermediate_cert.pem --path /cloudfront/my_cert_name_here/  

NOTE : Note the file:// and the trailing / in the command.

Once the SSL certificate is uploaded, you can head back to your CDN distribution on AWS Cloudfront and select “Custom SSL Certificate (stored in AWS IAM)” and “Only Clients that Support Server Name Indication (SNI)” options.

Now you should be able to see your assets both on HTTP and HTTPS served from CDN. Test it with CURL:

curl -I https://cdn.myawesomeapp.com/assets/image.png  

You can now safely change your config.action_controller.asset_host in your production.rb to //cdn.myawesomeapp.com

Khash Sajadi

Khash is the founder and CEO of Cloud 66, a full stack container management as a service. Follow him on @khash

London, UK and San Francisco, US
Subscribe and get updates

Have feedback? Please get in touch @cloud66 on Twitter.

Everything you need to build, manage and maintain containers in production on your own servers and any cloud

Try Cloud 66 — 14 Days Free Trial, No credit card required