在PHP中回答HTTP_IF_MODIFIED_SINCE和HTTP_IF_NONE_MATCH

时间:2010-01-04 16:52:54

标签: php caching header http-headers

我有可在PHP 5.1.0+中制作的可缓存动态内容。我已经将正确的标题(包括Last-Modified和ETag)发送给客户。

我现在希望我的脚本能够在出现时回复$_SERVER['HTTP_IF_MODIFIED_SINCE']$_SERVER['HTTP_IF_NONE_MATCH']。当条件匹配时,我想向客户回答HTTP 304 "Not Modified"

正确的条件是什么?我究竟发出304而不是整个内容?

有问题的答案 How to know when to send a 304 Not Modified response似乎正确地发出了这个问题,但我很难将该代码移植到PHP 5。

谢谢!

7 个答案:

答案 0 :(得分:31)

我一直用:

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();
        }
    }
}

不记得我是写它还是从其他地方拿到它......

我通常以这种方式在文件的顶部使用它:

caching_headers ($_SERVER['SCRIPT_FILENAME'], filemtime($_SERVER['SCRIPT_FILENAME']));

答案 1 :(得分:3)

您所引用的答案似乎包含了您所需要的一切。总结一下:

  • 生成您自己的ETag和Last-Modified标头,就像您要发送整个身体一样
  • 查看客户端发送的If-Modified-Since标头,如果您自己的最后修改版本较旧或同样发送304
  • 查看客户端的If-None-Match标头,如果它与您自己的ETag匹配发送304
  • 如果你到达这个地方,标题不匹配,发送完整的正文和新的ETag / Last-Modified标题

答案 2 :(得分:1)

这是我的render_file()函数的片段。

$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  

答案 3 :(得分:1)

如果我可以对Rich Bradshaw https://stackoverflow.com/users/16511/rich-bradshaw

最初的出色回答进行一些改进

调整了此代码,现在100%通过了If-Modified-Since和If-None-Match检查。它还可以正确格式化“最后修改日期”,因为原始答案最后会发送+0000而不是GMT,并将VARY标头添加到304响应中。您可以在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']));
?>

答案 4 :(得分:0)

  

如果客户已经执行了   条件GET请求和访问是   允许,但文件还没有   修改后,服务器应该响应   使用此状态代码。 304   响应不得包含   消息体,因而永远   由第一个空行终止   在标题字段之后。

从 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5

所以,如果发送304不发送正文。

答案 5 :(得分:0)

This article将回答有关缓存的所有问题

我发现添加了

RewriteRule .* - [E=HTTP_IF_MODIFIED_SINCE:%{HTTP:If-Modified-Since}]
RewriteRule .* - [E=HTTP_IF_NONE_MATCH:%{HTTP:If-None-Match}]

在我的htaccess文件的底部(在所有rewriterule下面)工作。

答案 6 :(得分:-3)

为什么呢?

在对这个主题做了大量研究之后,我发现条件请求实际上会减慢网站的速度。在某些情况下情况并非如此,但总体而言映射到一般使用模式会导致吞吐量降低和缓存效率降低。

下进行。