Serving Compressed Content with Rails and Elastic Beanstalk

When it comes to loading time of your web application speed is always a major aspect. A simple web site that loads very fast is the best user experience (UX). Speed is always in style. If you wonder whether your web site is fast or slow, you can always ask Google for that. Simply go to the Google PageSpeed Insights, enter your website URL and they will analyze it for you. That handy online tool checks your website against a list of bad practices and creates an overall score and detailed report for you. One of the metrics it uses to measure your web site speed is compression. Compression normally shrinks a text file by a factor. Given most web sites receive more hits from mobile devices than desktop browsers, shrinking your content becomes of a great importance. So, let’s see how to make your web application smaller and faster.

Why AWS Elastic Beanstalk?

I don not have to manage infrastructure. I focus on application development instead.

AWS Elastic Beanstalk (EB) is an easy-to-use service for deploying and scaling web applications. You really don’t need an Ops team to manage your deployments. Even a web developer with zero experience in infrastructure can manage to deploy his application on AWS EB. And the pricing is lower than what you will be charged for similar experience on popular PaaS like Heroku or Openshift.

There is no additional charge for Elastic Beanstalk - you pay only for the AWS resources needed to store and run your applications.

Serving Compressed Content

You have two problems to solve: serving static content and serving dynamic content. Let’s tackle them one at a time.

Static Content

Here you have a number of options. Just to name a few:

  1. Use of Content Delivery Network (CDN)
  2. Use of front-end proxy to handle the compression
  3. Use of web server to serve the pre-compressed assets

The approach I take on with Ruby on Rails applications on AWS EB is the simplest thing that works - do nothing. Yes, job done out of the box. Thank you, Amazon! Thank you, Rails team! But enough with the kudos and let’s look under the hood.

As assets may change only between deployments you have to setup everything during each deployment. There are two tasks on the to-do list here: compress the assets and serve the compressed version.

1. Compress the assets

Luckily the Rails asset pipeline handles this one for us by default.

By default, gzipped version of compiled assets will be generated, along with the non-gzipped version of assets.

If you ssh to any of your EC2 instances you can verify that.

$ ls public/assets/
application-225482854f70e3b5a0286fc874a35769ad5820f6176bb88f6440c14c472d3388.js
application-225482854f70e3b5a0286fc874a35769ad5820f6176bb88f6440c14c472d3388.js.gz
application-775bd24ca6f1e0684e3f2f04f9d0793df0860346c95558acabca4f67e48e70a0.css
application-775bd24ca6f1e0684e3f2f04f9d0793df0860346c95558acabca4f67e48e70a0.css.gz

As I already said - nothing to do at this step.

2. Serve the compressed version of the assets

Luckily again this time AWS EB handles this one for us by default. When you create your application environment, a Nginx configuration file is created for you: /etc/nginx/conf.d/webapp.conf (could be different in different versions of EB) with the following configuration inside:

  location /assets {
    alias /var/app/current/public/assets;
    gzip_static on;
    gzip on;
    expires max;
    add_header Cache-Control public;
  }

  location /public {
    alias /var/app/current/public;
    gzip_static on;
    gzip on;
    expires max;
    add_header Cache-Control public;
  }

Notice here the gzip_static setting which tells Nginx reverse proxy to serve compressed version of the assets. Nothing to do in here as well.

We managed to serve compressed static content with zero effort.

Dynamic Content

Let’s take a look at compressing dynamic content. Here again we have several options and just to name a couple:

  1. Rack::Deflater Middleware
  2. Let the reverse proxy handle compression

Using any of these approaches is better off to serving uncompressed content. I don’t want my Rails application (or application server) to be worrying about compression. I would prefer to delegate that job to a reverse proxy. On AWS Elastic Beanstalk it is quite easy to configure the Nginx reverse proxy to compress your application dynamic content. You can achieve that using ebextensions to create additional Nginx configuration files which will be included in the main configuration file like this:

include /usr/share/nginx/modules/*.conf;

That line from the main config file causes all conf files in the /etc/nginx/conf.d/ directory to be included in the main Nginx configuration. You add a new configuration file by creating a file under the .ebextensions folder in your application. For example, you create a file .ebextensions/01_gzip.conf like this:

files:
  "/etc/nginx/conf.d/gzip.conf":
      mode: "644"
      owner: "root"
      group: "root"
      content: |
        gzip on;
        gzip_types text/plain text/html application/json;

container_commands:
  01_reload_nginx:
    command: service nginx reload

That file will create a 01_gzip.conf file inside the Nginx conf.d folder that will be included by the main configuration file. The container command will make Nginx reload its configuration. As order matters, you may want to prefix your configuration files with a number to be certain of their order of inclusion. As my gzip.conf file is my first and only one I have named it 01_gzip.conf.

Back to serving gzipped content. You have two options that you have to configure on Nginx to compress your application dynamic content - gzip on; and gzip_types. Since we already serve compressed assets using gzip_static on here you may want to list only your dynamic content types. There are a number of other gzip options you may want to play around with here but those two are enough for your reverse proxy to start serving gzipped content. That is how you use EB extensions to configure your environment from your application source code.

Conclusion

With minimal efforts we were able to shrink our application content by a factor. Users love our web application even more than before. Bots love it. We just made the whole world of humans and machines a better place :)