我有一个 PHP 文件,它每次都将返回相同的东西并使用相同的 $_GET 参数——它是确定性的。
不幸的是,为了提高效率(这个文件经常被请求),每当请求一个 PHP 页面时,Apache 都会默认返回“200 OK”响应,从而使用户再次下载该文件。
当且仅当参数相同时,有没有办法发送304 Not Modified
标头?
奖励:我可以设置一个过期时间,以便如果缓存页面超过三天,它会发送“200 OK”响应?
我有一个 PHP 文件,它每次都将返回相同的东西并使用相同的 $_GET 参数——它是确定性的。
不幸的是,为了提高效率(这个文件经常被请求),每当请求一个 PHP 页面时,Apache 都会默认返回“200 OK”响应,从而使用户再次下载该文件。
当且仅当参数相同时,有没有办法发送304 Not Modified
标头?
奖励:我可以设置一个过期时间,以便如果缓存页面超过三天,它会发送“200 OK”响应?
如果不自己缓存页面(或至少是其 Etag),您将无法真正使用 304。成熟的缓存算法有点超出范围,但总体思路:
<?php
function getUrlEtag($url){
//some logic to get an etag, possibly stored in memcached / database / file etc.
}
function setUrlEtag($url,$etag){
//some logic to get an etag, possibly stored in memcached / database / file etc.
}
function getPageCache($url,$etag=''){
//[optional]some logic to get the page from cache instead, possibly not even using etag
}
function setPageCache($url,$content,$etag=''){
//[optional]some logic to save the page to cache, possibly not even using etag
}
ob_start();
$etag = getUrlEtag($_SERVER['REQUEST_URI']);
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
header("HTTP/1.1 304 Not Modified");
exit;
}
if(($content=getPageCache($_SERVER['REQUEST_URI'],$etag))!==false){
echo $content;
exit;
}
?>
//the actual page
<?php
$content = ob_get_clean();
setUrlEtag($_SERVER['REQUEST_URI'],$etag=md5($url.$content));
function setPageCache($_SERVER['REQUEST_URI'],$content,$etag);
header("Etag: $etag");
echo $content;
?>
所有常见的陷阱都适用:您可能无法为登录用户显示缓存页面,部分内容的缓存可能更可取,您自己负责防止缓存中的陈旧内容(可能在后端或数据库中使用触发器进行修改,或者只是玩弄getUrlEtag
逻辑)等等等等。
HTTP_IF_MODIFIED_SINCE
如果这更容易控制,你也可以玩弄。
通常,您使用 Header 函数返回 HTTP 状态代码:
Header("HTTP/1.1 304 Not Modified");
exit();
然而,仅此还不够。
问题是您不知道文件是如何请求的,因此您需要一些浏览器配合。
您可以If-modified-since
在传入请求中查找标头,如果存在且在日期范围内,则返回适当的状态代码。
如果您Expires
在最初生成 PHP 时发送了正确的标头,则浏览器或代理缓存可能会决定根本不获取请求(尽管更有可能,它们会设置If-modified-since
标头)。如果没有Expires
标头,浏览器可能总是会重新尝试完整的请求。
有关详细信息,请参阅http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html并搜索“14.25”
浏览器会将参数映射GET
到缓存副本,顺便说一句。你不需要在那里做任何工作。
您是否尝试过header("HTTP/1.0 304 Not Modified");
被调用的 PHP 代码?如果不熟悉,您将希望在开始向缓冲区输出任何内容之前将其放入代码中。
该脚本将再次渲染整个 PHP 脚本,但之后,它会检查 ETag 是否等同于输出字符串的 MD5,如果是,则发送 304 并且不使用带宽。您还可以使用所有 QueryString 等的 MD5 创建这样的东西并将其存储在某处,您无需重新创建输出内容(甚至更快)
function sanitize_output($buffer) {
$headers = apache_request_headers();
$tt5=md5($buffer);
header('ETag: '.$tt5);
if (isset($headers['If-None-Match']) && $headers['If-None-Match']===$tt5) {
header('HTTP/1.1 304 Not Modified');
header('Connection: close');
exit();
}
return $buffer;
}
ob_start("sanitize_output");