Host your Rails assets to Amazon S3 and CloudFront CDN

Few of Amazon AWS services are now hard requirement of any application. Out of all Amazon AWS services S3, CloudFront and EC2 are widely used services.

I am working on product since long time and now it’s time where I need to do performance optimisation. Performance is very important aspect of any web application. There are various factors affecting to performance and one of them is loading your static assets like Javascript, Stylesheets, Images etc of web application.

So to reduce page loading time hosting your asset behind the CDN (Content delivery network) is the best solution as per my opinion. I am going to show you how easily we can configure S3 and CloudFront in our existing Rails application and make it working.

S3 provides storage and isn’t really work as CDN. So S3 will store your assets and CloudFront will be used as our CDN which will play important role. Here CloudFront manages your different location request from it's network. So from whenever in the world user is located CloudFront will use that same location network to surve that user request which increases our website performance. One another thing is in first request CloudFront fetches asset from S3 and cache it. So in second request it surve the cached asset.

I am running with Rails 5.x and Ruby 2.6 currently and hosted my application on Heroku. I will use gem `asset_sync` (which isn’t recommended by heroku)

AssetSync gem will precompile our gem and push it to our S3 bucket and our S3 bucket is connected with CloudFront which will serve to our user.

Here are steps of configuration :

  1. Create a bucket with name my-testing-bucket (you can pick any name. But also consider S3 bucket naming conventions)

  2. Create CloudFront network and select our S3 bucket my-testing-bucket.

  3. Add gem ‘asset_sync' and ‘fog-aws’ in your Gemfile

  4. Create initializer file asset_sync.rb in config/asset_sync.rb

  5. Write following configuration in config/asset_sync.rb

if !Rails.env.development?

 AssetSync.configure do |config|

  config.gzip_compression = true

  config.manifest = true

  config.existing_remote_files = 'keep'

  config.fog_provider = 'AWS'

  config.aws_access_key_id = ENV.fetch('AWS_ACCESS_KEY_ID')

  config.aws_secret_access_key = ENV.fetch('AWS_SECRET_ACCESS_KEY')

  config.fog_directory = ENV.fetch('S3_ASSET_DIRECTORY')

  config.fog_region = ENV.fetch('AWS_REGION') #"eu-west-1"

  # Change host option in fog (only if you need to)

  config.fog_host = ENV.fetch('AWS_ENDPOINT') #'s3.amazonaws.com'

  config.fog_path_style = true

  config.run_on_precompile = false # https://github.com/AssetSync/asset_sync#rake-task

 end

end

6) Make sure you have added your AWS credentials to your environment file.

I am using figaro gem to manage environment variables in local machine.

7) Open config/environment/production.rb file and configure asset host

 config.action_controller.asset_host = ENV.fetch('AWS_CLOUD_FRONT_URL')

 config.action_mailer.asset_host = ENV.fetch('AWS_CLOUD_FRONT_URL')

8) Now commit your code and push to heroku

9) While pushing to heroku it will show you warning about not to use ‘asset_sync’ gem but you can ignore it.

10) asset_sync gem provides rake task that you can use it to push your asset to s3. Execute that rake task

‘heroku run rake assets:sync’ # This will precompile our assets and push to S3

‘heroku run rake assets:sync:clean’ # This will clear all unwanted assets from S3

11) You are done. Try to access your page and see in page source that you are getting assets from CloudFront.

12) There is only one way to clear cache in CloudFront is to add invalidation in CloudFront. I have simply written ‘/’ in that for my testing purpose. CloudFront invalidation charges money, so before using it make sure you know the costing of it.

Note : You can disable precompile your assets on heroku.

 

Hope that helps!