PHP Developer / Blog

August
17th, 2008

The impact of 304 Not Modified and content types on caching with Apache

Digg this article · Save to del.icio.us · Stumble it!

While in India on vacation I spent some time on front end performance enhancements.  I have apache set to cache static content fairly aggressively.  I blogged about how I went from an F to an A in YSlow.  I was surprised when I noticed that my combined and minified JavaScript and CSS files were not being cached or compressed.  It turned out that Apache wasn’t honoring my ExpiresByType Directives for JavaScript or CSS.  Solving the problem led me to a better all around solution for serving up JavaScript and CSS.

First the caching
I was using a dynamically constructed JavaScript/CSS file that would join together multiple sources and minify them.  To keep from having to do this for every page request I stored the result in Memcached using a Read-Through cache.

  if($cache = $memcached->get('js'))
  {
    echo $cache;
    exit;
  }

  foreach($files as $file){
    $out .= minify(file_get_contents($file));
  }
  $memcached->set('js', $out);
  echo $out;
  exit;

Far from ideal but it did help drastically.  The amount of JavaScript and CSS sent over the wire was reduced and I was happy with that.  But it should only have to be sent over the wire once per user and that’s what I thought it was doing until I noticed that JavaScript and CSS files were getting a HTTP 200 OK status code.  That meant they weren’t being cached at all and the data was being sent on every page request for a single user.

I decided to take another look at the implementation.  I was using a similar routine for both JavaScript and CSS files.  I was happy with that since requests would gracefully fail and Memcached was automagically updated as the contents changed.  So I wanted to keep that part but improve on how it was being stored.

The solution I ended up on was flat files.  Sure I came back full circle but sometimes you can end up where you started and be much better off.  The only modification I had to make was to write to disk instead of writing to Memcached. I was able to simplify my code.

  foreach($files as $file){
    $out .= minify(file_get_contents($file));
  }
  file_put_contents($out, md5(implode('|', $files));
  echo $out;
  exit;

  // additional utility function
  function getJS(){
    if(file_exists($src='/js/cachekey-aaa.js|myfile.js'))
      return '/js/static/'.md5($src).'.js';
    else
      return $src;  // this creates the static file JIT
  }

Now my ExpiresByType directive was successfully returning a 304 - Not Modified response.  Here’s an example of the two requests and their responses (goes same for CSS).

Old:
ExpiresByType text/javascript "A604800"
/js/compress-aaa.js|prototype.lite.js|javascript.js
HTTP 200 OK
 
New:
ExpiresByType application/javascript "A604800"
/js/static/5a3cfbb1ecf8415e5edecacf90bd391a.js
HTTP 304 Not Modified

Second the content type
I was surprised to see a production site returning different HTTP response codes then my development environment (my laptop).  Since I knew the caching directives were the same I was perplexed at first.  My laptop is running OSX with apache2 installed by macports.  Production servers are running Gentoo Linux with Apache2 installed by portage.  Just so happens that Apache on Gentoo was sending a content-type of application/x-javascript.  As a result the ExpiresByType directive wasn’t being honored.

Both of these issues were keeping the site from caching how I thought they were.  Just goes to show the importance of keeping an eye on things.  In the bigger picture it’s trivial but it may help one day to be prepared.

Leave a Reply

Captcha
Enter the letters you see above.


About this site:
This is my (Jaisen Mathai) personal site for potential employers who want to see my resume or portfolio. My ideal job would be to work as a PHP developer on a large scale consumer website. My experience is in using PHP, MySQL, Ajax and JSON. I really enjoy creative brainstorming...taking a problem apart and narrowing 100 solutions down to the best one.

Thanks for stopping by. Be sure to drop me a line.