Adding Expires Headers with OSCache's CacheFilter
A couple of weeks ago, I wrote about how I improved this site's YSlow grade by concatenating JavaScript and CSS with wro4j. Even though I loved the improvements, there was still work to do:
I'm now sitting at a YSlow (V2) score of 75; 90 if I use the "Small Site or Blog" ruleset. I believe I can improve this by adding expires headers to my images, js and css.
Last Monday, wro4j 1.1.0 was released and I thought it would solve my last remaining issue. Unfortunately, it only adds expires headers (and ETags) to images referenced in included CSS. Of course, this makes sense, but I thought they'd add a filter to explicitly add expires headers.
Since I still wanted this feature, I did some searching around and found what I was looking for: OSCache's CacheFilter. It was surprisingly easy to setup, I downloaded OSCache 2.4.1, added it to my WEB-INF/lib directory, and added the following to my web.xml.
<filter> <filter-name>CacheFilter</filter-name> <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class> <init-param> <param-name>expires</param-name> <param-value>time</param-value> </init-param> <init-param> <param-name>time</param-name> <param-value>2592000</param-value> <!-- one month --> </init-param> <init-param> <param-name>scope</param-name> <param-value>session</param-value> </init-param> </filter> <filter-mapping> <filter-name>CacheFilter</filter-name> <url-pattern>*.gif</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CacheFilter</filter-name> <url-pattern>*.jpg</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CacheFilter</filter-name> <url-pattern>*.png</url-pattern> </filter-mapping>
After restarting Tomcat and clearing out my Firefox cache, I was in business.
I did experience one issue along the way when I tried to remove the oscache.jar from my WEB-INF/lib directory. I'm using the JSPWiki Plugin and it seems to rely on a class in oscache.jar. I'm not sure which version oscache.jar is, but the packages got moved around somewhere along the way. The good news is it seems OK to have both oscache.jar and oscache-2.4.1.jar in Roller's classpath.
After discovering the duplicate JARs issue, I got to thinkin' that EhCache would probably have a solution. Sure enough, it has a SimpleCachingHeadersPageCachingFilter. Since I already had a working solution, I didn't bother trying EhCache (especially since my Roller install uses EhCache 1.1 and the filter is only available in a later version). However, when I implement expires headers in AppFuse, I'll definitely try EhCache's solution.
As for my YSlow score, it didn't improve as much as I'd hoped (low 80s instead of mid 80s). Some of this is due to my embedded presentation from Slideshare. There's also some external images I'm using in my Lightbox JS implementation. So if I can find a better Lightbox implementation (supports rel="lightbox" syntax), there's a good chance I'll switch. In the meantime, I'm lovin' how much faster this site loads.
In case you're wondering, I do plan on adding css/js concatenation and expires headers to both AppFuse 2.1 and Roller 5.
Update: FWIW, I did try to configure expires headers in Apache, but the AJP 1.3 Connector doesn't seem to allow this to work. To quote Keith from KGB Internet:
I added an expires directive and it didn't touch the header for anything served from Tomcat, but does for content served directly by Apache. This might have to be set up in Tomcat.
You should be able to add the expires header to AJP served content. We do it relatively often. What configuration did KGB use?
Cheers,
Matthew
Posted by Matthew Porter on November 24, 2009 at 12:02 PM MST #
Matt's web site is directed to Tomcat with a single directive from Apache:
with workers.properties looking like:
Keeping in mind that Matt's site has been on this same server since 2002, so it is still running Apache 1.3 and the compatible JK connectors for that version of Apache.
The documentation I have seen for adding expires when using the Apache 1.3 compatible connectors all revolve around using Filters.
For accounts on newer servers, we have instead moved to using mod_proxy, as we found the JK connectors became unstable (Apache throwing segmentation faults) with many virtual machines on the same physical box.
Posted by Keith Bjorndahl on November 25, 2009 at 03:59 AM MST #
With OSCache, the default scope cache is application.
I miss something : if images are statics why the default scope (application) was not selected ?
what was the requirement to use instead the session scope (according to your web.xml file) ?
au revoir
Posted by Farid C on April 02, 2010 at 09:49 AM MDT #
Posted by Matt Raible on April 02, 2010 at 03:57 PM MDT #