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 t开发者_C百科o PHP 5.
Thank you!
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']));
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
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
If I could improve slightly on the original brilliant answer from Rich Bradshaw https://stackoverflow.com/users/16511/rich-bradshaw
This code is tweaked and now 100% passes the If-Modified-Since and If-None-Match checks. It also correctly formats the Last-Modified Date as original answer sends out +0000 on the end instead of GMT and adds the VARY header to the 304 response. You can test this at redbot.org
<?php
function caching_headers ($file, $timestamp) {
$lastModified=filemtime($_SERVER['SCRIPT_FILENAME']);
$gmt_mtime = gmdate("D, d M Y H:i:s T", $lastModified);
header('ETag: "'.md5($timestamp.$file).'"');
header('Last-Modified: '.$gmt_mtime);
header('Cache-Control: must-revalidate, proxy-revalidate, max-age=3600');
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');
header("Vary: Accept-Encoding,User-Agent");
exit();
}
}
}
caching_headers ($_SERVER['SCRIPT_FILENAME'], filemtime($_SERVER['SCRIPT_FILENAME']));
?>
If the client has performed a conditional GET request and access is allowed, but the document has not been modified, the server SHOULD respond with this status code. The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields.
From - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
So, if you send a 304 don't send the body.
This article will answer all your questions on caching
I found that adding
RewriteRule .* - [E=HTTP_IF_MODIFIED_SINCE:%{HTTP:If-Modified-Since}]
RewriteRule .* - [E=HTTP_IF_NONE_MATCH:%{HTTP:If-None-Match}]
To the bottom of my htaccess file (below all rewriterule) worked.
Why?
Having done a lot of research on the subject I found that conditional requests actually slow down a site. There are certain scenarios where that is not the case, but mapping to general usage patterns overall it results in lower throughput and less effective caching.
C.
精彩评论