Improving AppFuse's PageSpeed with Apache
One of the most important things when developing webapps is to make them fast. With AppFuse, we've tried to incorporate many of the 14 rules for faster-loading websites. We had a gzip filter before it was cool (2003) and replaced it with the one from EhCache. However, users experienced issues with both of these, both with XFire/CXF and WebWork/Struts 2 and JSPs. Because of these issues, we disabled gzipping a few releases ago.
This article is designed to show you how you can make your AppFuse webapp faster, without modifying any code. The good news is this applies to any webapp that you can deploy behind Apache.
Last Friday, I sent an email to the good folks at Contegix to see if they could install mod_pagespeed on the Apache server that sits in front of *.appfuse.org. My goal was to improve the YSlow and PageSpeed scores of the apps hosted on demo.appfuse.org. I discovered they were getting a dismal score of 24 and figured we could do a lot better. mod_pagespeed speeds up your site and reduces page load time by automatically applying web performance best practices. It seemed like an easy solution.
Unfortunately, we were unable to use mod_pagespeed. From the guys at Contegix:
Attempting to install mod_pagespeed as you requested, we find that it requires Apache httpd 2.2 and libstdc++ 4.1.2, both of which are unsupported in RHEL4. To get mod_pagespeed to work on your present operating system basically means re-rolling the core components, which would make them unsupported. I'm afraid mod_pagespeed is simply not an option on your present configuration.
Since I still wanted to improve performance, I opted for another route instead: using mod_deflate (for gzipping) and mod_expires (for expires headers). I also turned on KeepAlive as recommended by PageSpeed Insights.
mod_deflate
mod_deflate was already installed in Apache (version 2.0.52), so all I had to do was configure it. On RHEL4, Apache is installed at /etc/httpd and there's a conf.d directory that contains all the configuration files. I created a file at /etc/httpd/conf.d/deflate.conf and populated it with the following:
# # mod_deflate configuration # <IfModule mod_deflate.c> SetOutputFilter DEFLATE AddOutputFilterByType DEFLATE text/plain text/html text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript DeflateCompressionLevel 9 BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch \bMSIE !no-gzip !gzip-only-text/html DeflateFilterNote Input instream DeflateFilterNote Output outstream DeflateFilterNote Ratio ratio LogFormat '"%r" %{outstream}n/%{instream}n (%{ratio}n%%)' deflate </IfModule>
At first, I had separate lines for all the different content types (as recommended by this article). The Contegix support crew figured out the solution (everything needed to be on one line) in 14 minutes, updated the config and verified it worked using an http compression testing page.
mod_expires
mod_expires was already installed, so I added a config file at /etc/httpd/conf.d/expires.conf. I used this howto and asked Contegix for help when it didn't work. Their response took quite a bit longer this time (49 minutes), but they once again figured it out:
It appears that FilesMatch does not like to play will JkMount. It does work using content type.
My final config for expires.conf:
<IfModule mod_expires.c> ExpiresActive On <FilesMatch "\.(jpe?g|png|gif|js|css)$"> ExpiresDefault "access plus 1 week" </FilesMatch> ExpiresByType image/jpeg "access plus 1 week" ExpiresByType image/png "access plus 1 week" ExpiresByType image/gif "access plus 1 week" ExpiresByType text/css "access plus 1 week" ExpiresByType application/javascript "access plus 1 week" ExpiresByType application/x-javascript "access plus 1 week" </IfModule>
I used "1 week" because we're changing things quite a bit right now and we haven't integrated resource fingerprinting yet.
KeepAlive
The last thing I did to improve performance was to turn on KeepAlive by editing /etc/httpd/conf/httpd.conf and changing Off to On.
# # KeepAlive: Whether or not to allow persistent connections (more than # one request per connection). Set to "Off" to deactivate. # KeepAlive On
Summary
As a result of these changes, our PageSpeed score went from 24 to 96 and YSlow went from a 90 to a 98. When I started this experiment, I was only trying to fix demo.appfuse.org. However, it also improved the speed of all the other *.appfuse.org sites, including Confluence, Bamboo, JIRA and FishEye. Thanks for all the help Contegix! There's a good chance you've given me back a few minutes in each day.
Originally posted on the AppFuse Blog.
Posted by Howard Lewis Ship on December 04, 2012 at 05:36 PM MST #
Posted by Matt Raible on December 05, 2012 at 03:36 AM MST #
Matt I think the best thing you can do for page load time today is to use pushstate technology like PJAX (https://github.com/defunkt/jquery-pjax) or DJAX (https://github.com/beezee/djax).
Using one of the above libraries requires minimal integration work and maintains your SEO. I'm actually surprised your blog isn't already doing it :)
Posted by Adam Gent on December 05, 2012 at 02:35 PM MST #
Adam - you are correct. We have PJAX on the AppFuse Roadmap and will hopefully add it sometime next year.
As for this blog, I'm planning on adding a mobile versions soon. I'll look into adding PJAX as well. Thanks for the inspiration!
Posted by Matt Raible on December 05, 2012 at 03:17 PM MST #
Some other things that I think would be cool that I don't see on the Roadmap (and I'll look into adding them in AppFuse Jira) are:
Posted by Adam Gent on December 05, 2012 at 04:30 PM MST #
Posted by Christopher Love on December 12, 2012 at 03:28 AM MST #
Posted by Matt Raible on December 12, 2012 at 03:29 AM MST #