Simon Willison’s Weblog

Supporting Conditional GET in PHP

This site’s RSS feeds now support Conditional GET. Since the feeds are dynamically generated on every request, adding support took a bit of hacking around with PHP. Here’s the function I came up with (based on the excellent description provided by Charles Miller in the article linked above):

function doConditionalGet($timestamp) {
    // A PHP implementation of conditional get, see 
    //   http://fishbowl.pastiche.org/archives/001132.html
    $last_modified = substr(date('r', $timestamp), 0, -5).'GMT';
    $etag = '"'.md5($last_modified).'"';
    // Send the headers
    header("Last-Modified: $last_modified");
    header("ETag: $etag");
    // See if the client has provided the required headers
    $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
        stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
        false;
    $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
        stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : 
        false;
    if (!$if_modified_since && !$if_none_match) {
        return;
    }
    // At least one of the headers is there - check them
    if ($if_none_match && $if_none_match != $etag) {
        return; // etag is there but doesn't match
    }
    if ($if_modified_since && $if_modified_since != $last_modified) {
        return; // if-modified-since is there but doesn't match
    }
    // Nothing has changed since their last request - serve a 304 and exit
    header('HTTP/1.0 304 Not Modified');
    exit;
}

Usage is simple: Work out the timestamp that the page content was last modified and call doConditionalGet($timestamp);. It will send the 304 header for you and exit if the client claims to have seen the content already—otherwise control will return to your main script and you can serve content as normal. Slightly inelegant, but it does the job.

Unfortunately I don’t have a Conditional-GET supporting RSS aggregator to hand so I have no idea if it works or not (so far I’ve only tested it by watching the headers sent with LiveHTTPHeaders). I’d be grateful if someone could confirm that this has had the desired effect.

Update: I’ve changed the above code sample (and my implementation) to send the ETag header as ETag rather than etag.

This is Supporting Conditional GET in PHP by Simon Willison, posted on 23rd April 2003.

Next: Acrobot

Previous: Entry Titles

Previously hosted at http://simon.incutio.com/archive/2003/04/23/conditionalGet