Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Answering HTTP_IF_MODIFIED_SINCE and HTTP_IF_NONE_MATCH in PHP

I have cacheable dynamic content made in PHP 5.1.0+. I already send the correct headers (including Last-Modified and ETag) to clients.

I now want my script to be able to answer $_SERVER['HTTP_IF_MODIFIED_SINCE'] and $_SERVER['HTTP_IF_NONE_MATCH'] when present. When the conditions matches, I want to answer a HTTP 304 "Not Modified" to clients.

What are the correct conditions? When exactly I issue a 304 instead of the whole content?

The accepted answer in question How to know when to send a 304 Not Modified response seems to issue this correctly but I have hard times to port that code to PHP 5.

Thank you!

like image 378
AlexV Avatar asked Jan 04 '10 16:01

AlexV


3 Answers

I've always used:

function caching_headers ($file, $timestamp) {
    $gmt_mtime = gmdate('r', $timestamp);
    header('ETag: "'.md5($timestamp.$file).'"');
    header('Last-Modified: '.$gmt_mtime);
    header('Cache-Control: public');

    if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
        if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] == $gmt_mtime || str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])) == md5($timestamp.$file)) {
            header('HTTP/1.1 304 Not Modified');
            exit();
        }
    }
}

Don't remember whether I wrote it or got it from somewhere else...

I'm normally using it at the top of a file in this way:

caching_headers ($_SERVER['SCRIPT_FILENAME'], filemtime($_SERVER['SCRIPT_FILENAME']));
like image 180
Rich Bradshaw Avatar answered Nov 08 '22 07:11

Rich Bradshaw


The answer you're referencing seems to contain all you need. To summarize:

  • generate your own ETag and Last-Modified headers, just as if you would be sending the whole body
  • look at the If-Modified-Since header the client sent, if your own last-modified is older or the same send the 304
  • look at the If-None-Match header of the client, if it matches your own ETag send the 304
  • if you reach this place, the headers did not match, send complete body and new ETag/Last-Modified headers
like image 45
Wim Avatar answered Nov 08 '22 06:11

Wim


Here is a snippet of my render_file() function.

$last_modified = filemtime($filename);
if ($last_modified === false) {
  throw new Exception('Modify date unknown');
}
if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
  $if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']));
  if ($if_modified_since >= $last_modified) { // Is the Cached version the most recent?
    header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified');
    exit();
  }
}
header('Last-Modified: '.date('r', $last_modified)); // tz should be GMT according to specs but also works with other tzs

// other headers and contents go here  
like image 39
Bob Fanger Avatar answered Nov 08 '22 07:11

Bob Fanger