Case Study: Increasing YSlow Score 30+ Points in Less Than One Hour
Ever since a client of mine asked me to look into the Firebug add-on YSlow, I've been interested in using it to increase performance on my Drupal (version 5) web sites. Wim Leer's recent posting about improving Drupal performance inspired me to take action to see what kind of improvements I could make.
Before I get into the details, please don't confuse me with an Apache guru. I know enough to modify various settings in an httpd.conf or an .htaccess file, but only after I've done my due diligence to make sure I'm not going to irrevocably screw things up.
While the title of this post indicates that I made all the changes in under an hour, in reality, it took me several hours to do all the research and testing. I'm hopeful that with the work I've done, other Drupal site admins will be able to make similar changes in under an hour.
I decided to focus on the low-hanging fruit. Since my site is small, I'm not looking to implement using a content delivery network (CDN). I'm also wanted to see what I can do with little or no changes to the site's code (other than the .htaccess file). With that in mind, I decided to look at the following categories:
- Make fewer HTTP requests
- Add an Expires header
- GZip components
- Minify JS
- Configure ETags
By improving these 5 categories, I was able to increase AnelloConsulting.com's YSlow score from an "F" (53) to a "B" (85).
Here's exactly what I did:
Turned on "Aggregate and compress CSS files"
This was a no-brainer and something that I had failed to do when I intially launched the site. As part of Drupal core (admin/settings/performance), it basically combines all of your site's various CSS files into a single file and compresses it (mainly by removing white space). Doing this reduces the number of files and the file size of your CSS definitions that the user has to download. This brought my YSlow score up from 53 to 60.
Configure "FileETag none"
According to Yahoo!'s Developer Network blog "Entity Tags (ETags) are a mechanism that web servers and browsers use to determine whether the component in the browser's cache matches the one on the origin server." The article goes on to say that unless you're explicity using them, it's best to just turn them off. You can do this by adding the following line in your .htaccess file:
Be warned, I found a couple of "top-o-the-search-page" references for ETags that have this definition incorrect (I'm looking at you, sitepoint). They indicate that the definition is "FileETags none" (note the plurality) - it took me a little while to figure out why this kept causing a "server not available" message on my site. Drupal 6 does not appear to include this definintion in the default .htaccess file. Configuring this brought my YSlow score up from 60 to 64.
Added far future Expires headers
This one was a little tricky for me, so I took a bit of conservative approach. The idea behind Expires headers is that when your server sends content (HTML, images, CSS, etc...) to a user's browser, it can tag each piece of content with an expiration date. This is ideal if you have a photo of you and your cat on your server that isn't going to be modified anytime soon. If you set the expiration date for that photo to be 2 years from now, then the next time the user visits your site (providing it is within 2 years and they haven't cleared out their browser's cache), they won't have to download the photo again, the browser will just pull it from its cache.
By default, Drupal has the following line in the .htaccess file:
ExpiresByType text/html A1
This basically says that for any file with the MIME-type of text/html, set the expiration date to 1 second past the current time of the user's computer ("A" indicates the user's computer, "1" indicates the number of seconds).
What I decided to do was to set the expiration date of some other MIME-types to 2 years in the future. So, this is what I modified the entire mod_expires block to:
ExpiresActive On
ExpiresByType text/html A1
ExpiresByType application/x-javascript "access plus 2 years"
ExpiresByType application/javascript "access plus 2 years"
ExpiresByType text/javascript "access plus 2 years"
ExpiresByType text/css "access plus 2 years"
ExpiresByType image/gif "access plus 2 years"
ExpiresByType image/jpeg "access plus 2 years"
ExpiresByType image/jpg "access plus 2 years"
ExpiresByType image/png "access plus 2 years"
The "text/html" line remains unchanges, but I added the "ExpiresActive On" line above it. This just ensures that the Expires header stuff is turned "on" (I believe it is "on" by default, but this line doesn't hurt). I also added a number of additional ExpiresByType lines to set far-future expiration dates for javascript, css, and image files.
There is was way to set the expiration date for all content at once, but as I said earlier, I decided to take a more conservative approach. Drupal 6 looks to take a slightly different approach - the .htaccess file that is slated to ship sets all files other than text/html to expire 2 weeks from their first access.
The only "danger" of this that I can figure is that when you update a file, if you don't change its filename, then a user who has it in their cache may not get the updated file. This might be especially important for updated CSS and JavaScript files when a theme or module is upgraded. However, I think this might be mitigated by the fact that the the CSS aggregation and the javascript_aggregator module (see below) both create aggregate files named by an MD5 hash, thus continually generate a new filename. Making this change to my .htaccess file brought my YSlow score from 64 to 79 - a whopping 15 points!
GZip components
Instead of a server sending a browser a plain uncompressed HTML text file, wouldn't it make more sense if it could be compressed on-the-fly by the server and uncompressed by the browser? Not surprisingly, this is (or should be) the standard way of doing things. Apache doesn't do this by default, but if you're using version 2.0, it's pretty easy to configure. All I did was add the following lines to my .htaccess file:
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/x-javascript application/json
Header append Vary Accept-Encoding
The first line tells the server to "DEFLATE" (compress) any HTML, CSS, TXT, XML, JavaScript, or JSON data before sending it to the browser. Since most image formats like GIF, JPG, and PNG are already compressed, there's no reason to add them to the list. The second line helps ensure that the server doesn't serve GZipped components to browsers that don't support it. Drupal 6 does not address GZipping components in its .htaccess file. If you're using Apache 1.x, then check out the previously mentioned sitepoint article for several options. Making this change brought my YSlow score from 79 to 84.
javascript_aggregator module
Finally, I decided to see what modules were out there to do for JavaScript what the CSS aggregator does for style sheets. The javascript_aggregator module does just about the exact same thing. Like the CSS Aggregator, it combines and compresses your site's JavaScript files in a bid to both reduce the number of files that must be downloaded as well as their overall size. The javascript_aggregator compresses JavaScript files only by removing comments and white-space. It doesn't "compress" JavaScript files like Dean Edward's most-excellent Packer, but there's talk of adding that to Drupal 7.
Installing the javascript_aggregator module is simple enough, just enable the module, then go to admin/settings/performance to turn it on and to configure its options. It allows you to specify particular JavaScript files that the aggregator should ignore (apparently TinyMCE's JavaScript doesn't play nicely with others). Installation of the module also requires a small, but easy change to your theme's page.tpl.php file. Enabling this module improved my YSlow score from 84 to 85, a solid "B".
I hope this information is useful, please let me know of your own results in the comments below!

hi there
Goed artikel, ik heb deze blog opgenomen op mij iGoogle!
not sure
good points
1. gzipping everything looks
Post new comment