Nearly all the big development news on the web over the past few years have been related to the rich experience and interaction users are coming to expect from the web sites. Web sites are no longer just flat presentation brochures that can be paged through like magazines, they are expected to flow and mold with users each click and do so without tremendous page reloads.
AJAX technology has made that possible through dozens of AJAX javascript frameworks. But those frameworks, handling everything from special interface effects to site functionality, come at a hefty price. While there are highly optimized javascript toolkits out there, it is not unusual for people to use several large libraries in their development. Without optimization that means downloading a few hundred KB each time you load the site or reload the page. Maybe not as painful on a desktop with broadband, but imagine a slower or mobile phone/pc connection. Not good news. However, if you are on a server running Apache, you are in luck!
Enter mod_expires
mod_expires is a module that can be built into Apache web server to control the caching (read: expiration) of items downloaded from the site. It is both compiled and enabled in Redhat Enterprise Linux package of Apache and all you have to do is enable it through .htaccess. Here is mine:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType application/x-javascript “access plus 1 month”
</IfModule>
That is all there is to it but let’s look closely at this because it is more powerful than it appears. First and last line are there just for the sake of error control, the <IfModule>..</IfModule> checks if mod_expires is available and if it is the code gets executed. This prevents your web application from crashing if it doesn’t have the required module available. The ExpiresByType is very powerful. In my example I am telling the browser to cache all javascript files it downloads for 1 month after the access.
This mechanism is very powerful even in applications beyond AJAX. For example, let’s say you design your site with the layout images in the gif format. Your template, things like bullets, borders, header images, icon art and more are all saved in the gif format. Since those are loaded each time a visitor accesses your site, caching template images can significantly decrease the load time and display your content faster. How is that for a benefit. Publish template files as .gif or .png but publish content inline as a .jpg so dynamic content loads on demand while the .gif or .png are cached because they rarely change.
Does it work?
Quick way to check if the mod expires works is to look at the headers. I use wget:
wget -S -O – http://support.ownwebnow.com/includes/checkform.js | more
Obviously replace the URL with your own. You will get something similar to the following:
HTTP request sent, awaiting response…
HTTP/1.1 200 OK
Date: Tue, 04 Dec 2007 04:34:34 GMT
Server: Apache/2.0.52 (CentOS)
Last-Modified: Sun, 08 Jul 2007 19:19:48 GMT
ETag: “b8c21a-304a-6960dd00”
Accept-Ranges: bytes
Content-Length: 12362
Cache-Control: max-age=2592000
Expires: Thu, 03 Jan 2008 04:34:34 GMT
Connection: close
Content-Type: application/x-javascript
Length: 12,362 (12K) [application/x-javascript]
Notice the expiration date: one month in the future. If you don’t see the Expires in the http headers something went wrong.
Handling code revisions
Obviously this works great for caching code that is not modified very often, such as your base application libraries. For example, cache the entire framework but do not cache your specific includes and extensions that you may actively modify. Here is an easy hack to get around that, just place your static code (that doesn’t change often) in a different directory.
<LocationMatch /inc>
ExpiresActive on
ExpiresByType application/x-javascript “access plus 1 month”
</LocationMatch>
While my original code will handle the expirations server-wide just by matching the content type (javascript), LocationMatch adds in an extra restriction so that only javascript from a particular directory (/inc) gets cached for 1 month.
In the worst case scenario, your library might need to be updated immediately. It happens. Security patches, urgent code changes, meeting deadlines and other external factors might force you to deploy code before the other libraries have expired. Not to worry, I have a trick for that as well. I keep my includes in a set of include files. If I ever need to roll a new file I just remove the old one and place the new one in with a new filename in a different directory. Makes change management a nightmare, but I have yet to be forced to use it and I don’t forsee a day that I will but its always best to be prepared.
There you go, a little bit of server content expiration trickery to decrease your bandwidth costs and your application load times.
Pingback: Vlad Mazek - Vladville Blog » Blog Archive » Howto: Speeding up AJAX web applications with htaccess and mod_expires