Rails output compression

December 7, 2005 Link to post  Permalink

I want to be able to compress my output text that I send to the browser from my Rails app. This will include HTML, javascript, Ajax responses, plain text etc. Lighttpd will perform this function for the static files it serves, but not for the dynamic files, so I went looking for a solution.

There is a README for ActionPack that has this snippet of code:

after_filter { |c| c.response.body = GZip::compress(c.response.body) }

I added this to my ApplicationController, and it doesn’t work.

I can’t find any GZip object in my Ruby or Rails code. I did find some output compression in the Ruby SOAP implementation, so I re-purposed that code for my after_filter. Here’s what I ended up with:

require ‘stringio’
require ‘zlib’

class ApplicationController < ActionController::Base
after_filter :compress

def compress if self.request.env[‘HTTP_ACCEPT_ENCODING’].match(/gzip/) if self.response.headers[“Content-Transfer-Encoding”] != ‘binary’ begin ostream = StringIO.new gz = Zlib::GzipWriter.new(ostream) gz.write(self.response.body) self.response.body = ostream.string self.response.headers[‘Content-Encoding’] = ‘gzip’ ensure gz.close end end end end


Some notes:

  • The request must be checked to see if the browser supports gzip encoding before sending the compressed data
  • I don’t do the gzip detection entirely to spec, if q=0 in the accept encoding header, GZIP should be disabled.

Since the request accept-encoding header must be checked, and the content-encoding header added, the original code example could never have worked correctly. My version of the code should work in most cases.

I added a check for the Content-Transfer-Encoding header. If this is ‘binary’, then don’t compress, as the response is an image, or something we don’t want to compress. Rails leaves this header off for text responses, like HTML.