---
title: Honey, I shrunk the internet! - Content Compression via Rack::Deflater
teaser:
tags: web,ruby,performance
author: Dan Collis-Puro
published_on: 2013-11-12
---

Speed is key. The snappier the site, the more visitors like you. Speed is so important that Google [uses it in site rankings](http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html) and as a major component of its [PageSpeed Tools](https://developers.google.com/speed/pagespeed/).

[Rack::Deflater](http://rubydoc.info/github/rack/rack/master/Rack/Deflater) middleware compresses responses at runtime using [deflate](http://en.wikipedia.org/wiki/DEFLATE) or trusty ol' [gzip](http://en.wikipedia.org/wiki/Gzip).  Inserted correctly into your Rack app, it can drastically reduce the size of your <abbr title="HyperText Markup Language">HTML</abbr> / <abbr title="JavaScript Object Notation">JSON</abbr> controller responses (numbers below!). On a Heroku Rails 3 deployment, it can be configured to compress assets delivered via your dynos.

There are other (possibly better) places to handle content compression, for example:

* A frontend proxy / load balancer,
* Your CDN,
* By pre-compressing content and serving that from your web server.

We're going to talk about the simplest thing that'll work for most Heroku hosted Rails apps.

## Rails 3 and 4

Add it to `config/application.rb` thusly:

```ruby
module YourApp
  class Application < Rails::Application
    config.middleware.use Rack::Deflater
  end
end
```

And your <abbr title="HyperText Markup Language">HTML</abbr>, <abbr title="JavaScript Object Notation">JSON</abbr> and other Rails-generated responses will be compressed.

## Rails 3 and Runtime Asset Compression

If you're running Rails 3 (which will serve static assets for you), you can use `Rack::Deflater` for runtime asset compression as well. Configure it thusly:

```ruby
module YourApp
  class Application < Rails::Application
    config.middleware.insert_before ActionDispatch::Static, Rack::Deflater
  end
end
```

Inserting `Rack::Deflater` before `ActionDispatch::Static` means you'll get runtime compression of assets served from Heroku in addition to the <abbr title="HyperText Markup Language">HTML</abbr>, <abbr title="JavaScript Object Notation">JSON</abbr>, <abbr title="Extensible Markup Language">XML</abbr> and other content your app returns.

Rails 4 assumes you're serving assets from a CDN or via your webserver (and not your Rails processes) so `ActionDispatch::Static` middleware isn't enabled by default. If you try to insert `Rack::Deflater` before it,  you'll get errors.

## spec'ing

Controller specs skip Rack middleware. You need to assert that content is compressed / not compressed in a feature spec as they exercise the full Rails stack.  We're using [Capybara's RSpec awesomeness](https://thoughtbot.com/blog/rspec-integration-tests-with-capybara) for our integration specs.  Example:

    # spec/integration/compression_spec.rb
    require 'spec_helper'

    feature 'Compression' do
      scenario "a visitor has a browser that supports compression" do
        ['deflate','gzip', 'deflate,gzip','gzip,deflate'].each do|compression_method|
          get root_path, {}, {'HTTP_ACCEPT_ENCODING' => compression_method }
          response.headers['Content-Encoding'].should be
        end
      end

      scenario "a visitor's browser does not support compression" do
        get root_path
        response.headers['Content-Encoding'].should_not be
      end
    end

## Compression Overhead - Dynamic Content

There is overhead to compressing content - let's see how significant it is. These tests were run against a fairly typical Rails 3.2 app.

We'll run [Siege](http://freecode.com/projects/siege) against a dynamic content page running under [Thin](http://code.macournoyer.com/thin/) for 30 seconds, simulating 10 concurrent users. I'm picking typical results: I ran Siege numerous times for each scenario.

    siege -t30s -c 10 'http://127.0.0.1:3000/contact'

|&nbsp;|Before Rack::Deflater|After Rack::Deflater|
|Transactions|271 hits|265 hits|
|Data transferred|1.24 MB|0.45 MB|
|Transaction rate|9.19 trans/sec|8.93 trans/sec|

So we were a *little* slower, but the content is around 1/3rd the size.

## Compression Overhead - Static Content

Let's try against static asset content.

    siege -t10s -c 10 'http://localhost:3000/assets/application.css'

|&nbsp;|Before Rack::Deflater|After Rack::Deflater|
|Transactions|22601 hits|4052 hits|
|Data transferred|658.26 MB|21.44 MB|
|Transaction rate|2374.05 trans/sec|443.33 trans/sec|

So when we benchmark the raw speed of compressing static assets, it's around 5 times slower. The benefit, though, is that application.css is around 6 times smaller - 6.2KB instead of 30.1KB.

Plus we're still serving 443 requests per second. That is far beyond the demands that'd be put on any one dyno, and at traffic levels like this you're probably already using a CDN.

## Real world impact

Once you've enabled `Rack::Deflater` middleware, you should see compression statistics in the Chrome web inspector for (at least) your Rails-generated content. For example:

![Compression Results](https://images.thoughtbot.com/content-compression-with-rack-deflater/compression_results.png)

Assets are also being compressed in this Rails 3.2 app via `Rack::Deflater`, hence the compression ratios for `application.css` and `application.js`.

|&nbsp;|Before Rack::Deflater|After Rack::Deflater|Compression Rate|
|Google PageSpeed analysis|79 of 100|93 of 100|N/A|
|application.css|30.1KB|6.2KB|79%|
|application.js|117.0KB|40.8KB|65%|
|page html|4.3KB|2.6KB|40%|
|Total size|151.4KB|49.6KB|67%|

So with minimal effort we were able to decrease our page sizes significantly (1/3rd of their original size!) and bump up our PageSpeed analysis 14 points.
