Latest: Genstatic, my first sip of coffee

Content with Style

Web Technique

Conditional get in PHP - with some problems on the way

by Pascal Opitz on January 1 2009, 18:05

Happy new year everyone. If you're anything like me, the credit crunch doomsday news and the gross overeating of the past festivities make you want to slim down and become more resourceful. Using Caching, ETags and the 304 HTTP code can help to cut down on traffic. But working on a conditional get came with some problems ...

The first issue I had is that Firefox didn't send the proper headers. I was't entirely sure why, but after a long google search and trial and error it turned out that it was happening as long as there weren't the correct Cache-Control or Pragma directives. Setting Cache-Control to 'public, must-revalidate' and Pragma to 'cache' got it finally working.

However, the PHP snippets that I looked at for reference utilize the HTTP_IF_MODIFIED_SINCE and HTTP_IF_NONE_MATCH entries in the $_SERVER array. Something that I could not get to work at all. Using the apache_request_headers() circumnavigates this issue, and the headers If-None-Match and If-Modified-Since are pulled correctly.

So here's the code that works for me as it stands:


function checkBrowserCache($identifier, $last_modified) {
  $arr = apache_request_headers();
  $etag = '"' . md5($last_modified . $identifier) . '"';
  $client_etag = @$arr['If-None-Match'] ? trim(@$arr['If-None-Match']) : false;
  $client_last_modified_date = @$arr['If-Modified-Since'] ? trim(@$arr['If-Modified-Since']) : false;
  $client_last_modified = date('D, d M Y H:i:s \G\M\T', strtotime($client_last_modified_date));

  $etag_match = true;    
  
  if(!$client_last_modified || !$client_etag) {
    $etag_match = false;
  }
  
  if($etag_match && $client_last_modified > $last_modified) {
    $etag_match = false;
  } 
  
  if($etag_match && $client_etag != $etag) {
    $etag_match = false;
  }

  header('Cache-Control:public, must-revalidate', true);
  header('Pragma:cache', true);
  header('ETag: '.$etag);

  if($etag_match) {
    header('Not Modified',true,304);
    die();
  }

  header('Last-Modified:'.date('D, d M Y H:i:s \G\M\T', $last_modified));
}

Still, one problem remained: Firefox refuses to do as all other browsers that I tested. After getting a 304 correctly, it will pull the page again in its entirety. This means a full 200 OK response every second time Firefox does a request.

Comments

  • This class in use in Minify may be handy. It does not use apache_request_headers() so it may not work w/o a little modification, but it's pretty well tested.

    by Steve Clay on January 1 2009, 22:30 - #

  • Thanks Steve.

    I am still trying to find out why the $_SERVER array doesn't return the values that everyone else seems to be using. Is there a setting that needs to be compiled into apache or PHP?
    Any ideas anyone? I am using MAMP as ell as CPANEL based hosting on CentOS. Apache 2 and PHP 5.

    by Pascal Opitz on January 1 2009, 22:55 - #

  • maybe $_SERVER only returns this value when register_globals is on? This is suggested in this phpbuilder forum thread. I know, it doesn't sound right.

    They also suggest to use getenv("HTTP_IF_MODIFIED_SINCE").

    Otherwise I can't see anything wrong with this, other than the fact that your sources are 3 and 5 years old. Maybe some apache and/or php setting has changed since.

    Regarding your double-request in firefox: did you try turning off firebug? I've had double requests (in cached/uncached pairs) because of it in the past.

    by Matthias Willerich on January 4 2009, 00:07 - #


Comments for this article are closed.