304:未修改和前端缓存

15
我正在使用PHP脚本来提供文件服务。如果客户端自上次下载以来未更改文件,我希望能够在我的HTTP响应中发送一个304未修改的标头。这似乎是Apache(和大多数其他Web服务器)中的一项功能,但我不知道如何通过PHP实现它。
我听说过使用$_SERVER ['HTTP_IF_MODIFIED_SINCE'],但是这个变量似乎没有出现在我的$_SERVER超级数组中。
我的问题不是如何返回304头,而是如何知道应该返回304头。
编辑:问题在于我的$_SERVER ['HTTP_IF_MODIFIED_SINCE']未设置。这是我的.htaccess文件的内容:
ExpiresActive On 
ExpiresByType image/jpeg "modification plus 1 month"
ExpiresByType image/png "modification plus 1 month"
ExpiresByType image/gif "modification plus 1 month"
Header append Cache-Control: "must-revalidate" 


<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteCond $1 !^(controller\.php)
   RewriteRule (.*\.jpg|.*\.png|.*\.gif) controller.php/$1
</IfModule>

HTTP_IF_MODIFIED_SINCE仍未出现在$_SERVER超级数组中。

6个回答

27

HTTP_IF_MODIFIED_SINCE 是正确的方法。如果你没有得到它,请检查 Apache 是否启用并正常工作 mod_expiresmod_headers。引用自 PHP.net 的一条评论

$last_modified_time = filemtime($file); 
$etag = md5_file($file);
// always send headers
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT"); 
header("Etag: $etag"); 
// exit if not modified
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || 
    @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
    header("HTTP/1.1 304 Not Modified"); 
    exit; 
}

// output data

1
我见过这种做法,但对我来说 $_SERVER['HTTP_IF_MODIFIED_SINCE'] 总是为空的... - jd.
只是一个小注释,如果有人遇到和我一样的问题:你必须始终定义lastModify头。我是在if语句之后才这样做的。(我知道这听起来很奇怪,因为缓存永远不会被删除,但这就是计划 :)) - haltabush
谢谢c_harm,有一个问题是我也需要转义trim()。如果你在Chrome中强制重新加载(shift-reload),那么Chrome就不会发送IF_MODIFIED头来获取新鲜数据。if()语句将检查trim()语句,但会返回一个错误,因为SERVER[NONE_MATCH]未设置(Chrome和其他浏览器不发送Etag头)。这一开始有点难以调试,因为我正在发送原始图像数据。 - GDmac
3
这个好回答非常重要的一部分是关于"Last-Modified"的“始终发送头文件”的问题。如果不发送此标头,您将永远无法在请求中获得HTTP_IF_MODIFIED_SINCE,因为浏览器不会发送它。 - Gabriel
好的!Last-Modified头在我的情况下起作用了。 - kabirbaidhya
显示剩余3条评论

1

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

我发现添加

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

将下面的代码添加到我的htaccess文件底部(在所有RewriteRule之后)即可生效。


1
我曾遇到过这个问题,后来发现是因为我打开了Firebug。在其Net选项卡下有一个“禁用浏览器缓存”选项,默认已被选中。Chrome的开发者工具中也有类似的选项,在菜单栏下方的复选框之一。
取消选中这些选项后,浏览器正确地发送了HTTP_IF_MODIFIED_SINCE,最终一切都正常运作(即使同时打开了Firebug或Chrome Dev Tools)。

0

还有一些其他参数需要检查...在我的情况下,我没有这两个标题:

$_SERVER['HTTP_IF_NONE_MATCH'] && $_SERVER['HTTP_IF_MODIFIED_SINCE']

我的系统时钟有点慢,所以需要返回正确的304头信息,否则会将这些页面解释为将来过期的页面,并最终不发送任何值。

还要检查Apache返回的头信息,或者覆盖它以使用更大的值。

Cache-Control: max-age=3600

如果不发送先前的标头,则如此

Last-Modified previous sent header < ( NOW - 3600 )

所以在我的情况下,我设置了这个非常方便的函数 function lastModified($file){ $x=filemtime($file); while($x>time())$x-=86000;}#如果在未来日期被触摸,则减少一天 $date=gmdate('D, j M Y H:i:s',$x).' GMT'; header('Cache-Control: max-age=86000',1); if($_SERVER['HTTP_IF_NONE_MATCH'] == $x || $_SERVER['HTTP_IF_MODIFIED_SINCE']==$date){ header('HTTP/1.1 304 Not Modified',1,304);die;} header('Etag: '.$x,1);header('Last-Modified: '.$date,1); }


0
请注意,$_SERVER["HTTP_IF_NONE_MATCH"] 可能包含引号和-gzip后缀。
$server_etag = str_replace("-gzip", "", str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])));

if ($server_etag == $etag) ...

0

$_SERVER['HTTP_IF_MODIFIED_SINCE']通常在register_globals关闭时为空。

检查是否是这种情况,如果是,请尝试使用getenv('HTTP_IF_MODIFIED_SINCE')


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