在PHP中回答HTTP_IF_MODIFIED_SINCE和HTTP_IF_NONE_MATCH请求头

12

我有一个可缓存的动态内容,使用的是PHP 5.1.0+。 我已经向客户端发送了正确的头信息(包括Last-Modified和ETag)。

现在,当客户端发送$_SERVER['HTTP_IF_MODIFIED_SINCE']$_SERVER['HTTP_IF_NONE_MATCH']时,我希望我的脚本能够响应。 当条件匹配时,我想向客户端返回一个 HTTP 304“Not Modified”

什么是正确的条件? 什么情况下我才会发送304而不是整个内容?

问题《如何知道何时发送304 Not Modified响应》中的被接受的答案似乎可以正确地处理这个问题,但我很难将该代码移植到PHP 5。

谢谢!

7个回答

33

我一直使用:

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']));

5
不错的函数,Rich!稍微易读一些:caching_headers (__FILE__, filemtime(__FILE__)); - Julian
1
非常有用!我尝试了这个,但是执行多个请求时会交替返回200和304。将“Last-Modified”和“Cache-Control”放在“ETag”定义之后可以解决这个问题。 - Benjamin Intal
最好检查一下是否设置了"HTTP_IF_NONE_MATCH",以避免在将空值传递给PHP 8.1中的stripslashes时出现PHP警告和未知错误。 - Taras
最好检查一下"HTTP_IF_NONE_MATCH"是否也被设置,以避免在将空值传递给PHP 8.1中的stripslashes时出现PHP警告和未知错误。 - undefined

3
您所参考的答案似乎包含了您需要的所有内容。总结如下:
  • 生成自己的ETag和Last-Modified头,就像您发送整个正文一样
  • 检查客户端发送的If-Modified-Since头,如果您自己的last-modified比它旧或相同,则发送304
  • 查看客户端的If-None-Match头,如果与您自己的ETag匹配,则发送304
  • 如果到达这个地方,头部不匹配,则发送完整的正文和新的ETag/Last-Modified头

2

这是我的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  

1
HTTP_IF_NONE_MATCH呢?你的代码片段应该在哪里加入它? - AlexV
1
修改日期对我来说已经足够验证了,计算 Etag(内容 md5/sha1 的校验和)会产生一些服务器开销。但是,Etag 出错的概率较小。 如果内容正确性很重要,请先检查 IF_NONE_MATCH。 如果 IF_NONE_MATCH 没有设置,则检查 IF_MODIFIED_SINCE。 如果 Etag 不匹配,请勿检查 IF_MODIFIED_SINCE。因为您知道浏览器缓存无效!只需发送 304 标头并退出()。 - Bob Fanger

1
如果我可以稍微改进原来来自Rich Bradshaw的杰出答案 https://stackoverflow.com/users/16511/rich-bradshaw 这段代码已经进行了调整,现在100%通过If-Modified-Since和If-None-Match检查。它还正确地格式化了Last-Modified Date,因为原始答案在末尾发送了+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']));
?>

0

这篇文章将回答你所有有关缓存的问题。

我发现添加

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

将以下代码添加到我的htaccess文件底部(在所有重写规则之下)即可生效。

0
如果客户端执行了条件GET请求并且允许访问,但文档未被修改,则服务器应该用此状态码响应。304响应不得包含消息正文,因此始终在标头字段后的第一行空行处终止。
来自-http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 因此,如果发送304,请勿发送正文。

我知道这一点,但我的问题不在于此... 我的问题是关于何时发送304的问题... - AlexV
啊,抱歉,得放慢速度,= ] - mr-sk
我的意思是在哪些情况下发送304。我寻求的答案示例是:“当HTTP_IF_MODIFIED_SINCE存在并匹配您的上次修改日期时,发送304,并且当HTTP_IF_NONE_MATCH存在并匹配您的ETags时”。 - AlexV

-3
为什么?
经过大量相关领域的研究,我发现条件请求实际上会减缓网站的速度。虽然在某些情况下并非如此,但总体而言,它会导致吞吐量下降和缓存效果不佳。
C.

为什么会减慢网站速度?我甚至无法想象它如何会减慢它(如果您发送正确的缓存头)... - AlexV
1
那个问题的简短回答是大约5页的文本和图表。我一直想把它放在互联网上的某个地方......敬请期待。 - symcbean
很有趣,期待看到 :) - AlexV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接