Rails performance tip – using YSlow

YSlow from Yahoo! is a Firefox add-on to analyse web pages and tell you why they’re slow based on rules for high performance web sites. YSlow requires the indispensable Firebug extension.

The 13 rules YSlow checks your site against are as follows:

1. Make Fewer HTTP Requests
2. Use a Content Delivery Network
3. Add an Expires Header
4. Gzip Components
5. Put CSS at the Top
6. Move Scripts to the Bottom
7. Avoid CSS Expressions
8. Make JavaScript and CSS External
9. Reduce DNS Lookups
10. Minify JavaScript
11. Avoid Redirects
12. Remove Duplicate Scripts
13. Configure ETags

This post will demonstrate that most of these are easily achievable for a Rails website through a combination of plugins and with correct configuration of a proxy web server (in front of a mongrel cluster) – in this case Nginx. This guide follows experience with improving performance for trawlr.com (an online RSS reader).

Make Fewer HTTP Requests, Minify JavaScript, Put CSS at the Top, Move Scripts to the Bottom, Remove Duplicate Scripts

The easiest way to make fewer HTTP requests is to combine all JavaScript and CSS files into one. The asset packager plugin does exactly this, plus it will also compress the source files (in production mode) and correctly handles caching (without query string parameters).

Moving CSS to the top (within the head section) and moving JavaScript to the bottom of the page are both manual tasks that should be done in the layout templates (such as app/views/layouts/application.rhtml). Remember to use stylesheet_link_merged :base and javascript_include_merged :base rather than the default Rails helpers.

By using asset packager you can also verify that scripts are only included once – another performance hit otherwise!

Excluding the Google analytics JavaScript file, trawlr.com now uses a single css and js file (including the entire prototype library). Note: You may need to add a missing semi-colon as per this defect for prototype to work correctly.

Asset Packager can be included as part of a Capistrano deployment with the following recipe:

desc "Compress JavaScript and CSS files using asset_packager"
task :after_update_code, :roles => [:web] do
  run <<-EOF
    cd #{release_path} &&
    rake RAILS_ENV=production asset:packager:build_all
  EOF
end

Use a Content Delivery Network

Ignoring this point for now; I’d suggest the use of Amazon S3 as a useful starting point for simple CDN.

Add an Expires Header

A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.

Nginx allows adding arbitrary HTTP headers via the expire and add_header directives. Adding the expires header to static content is done with a regular expression looking for relevant file extensions in the request URL. This example uses the maximum expiry date but could be set to more appropriate values as required (e.g. 24h, 7d, 1M)

# Add expires header for static content
location ~* \.(js|css|jpg|jpeg|gif|png)$ {
  if (-f $request_filename) {
        expires      max;
    break;
  }
}

Gzip Components

Nginx can gzip any responses – including those proxied from a mongrel cluster.

gzip on;
gzip_min_length  1100;
gzip_buffers     4 8k;
gzip_proxied any;
gzip_types  text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

Avoid CSS Expressions

Just don’t do it!

Make JavaScript and CSS External

Add you JavaScript and CSS styles in external files rather than inline. The added benefit here is that the content will be merged and compressed thanks to the work already done above.

Reduce DNS Lookups, Avoid Redirects, Configure ETags

These weren’t an issue for me so I suggest the Yahoo! guidance for further information

Reduce DNS Lookups

Avoid Redirects

Configure ETags

Summary

After making the changes outlined above the YSlow score for trawlr.com has hit a B grade (89) with all points A grade except “Use a CDN” which I have not addressed. The “Stats” view indicates that with an empty browser cache there would be 30 HTTP requests (26.0K total size), with a full cache this drops to a single request (the HTML document) (6.5K total size). Worth the effort in my opinion!


About this entry