如何在PHP中使用HTTP缓存头

如何在PHP中使用HTTP缓存头,php,http,caching,Php,Http,Caching,我有一个PHP5.1.0网站(实际上是5.2.9,但也必须在5.1.0+上运行) 页面是动态生成的,但其中许多页面大多是静态的。静态的意思是内容不会改变,但是内容周围的“模板”会随着时间的推移而改变 我知道已经有了一些缓存系统和PHP框架,但我的主机没有安装APC或Memcached,我也没有为这个特定的项目使用任何框架 我希望页面被缓存(我认为默认情况下PHP“不允许”缓存)。到目前为止,我正在使用: session_cache_limiter('private'); //Aim at 'pu

我有一个PHP5.1.0网站(实际上是5.2.9,但也必须在5.1.0+上运行)

页面是动态生成的,但其中许多页面大多是静态的。静态的意思是内容不会改变,但是内容周围的“模板”会随着时间的推移而改变

我知道已经有了一些缓存系统和PHP框架,但我的主机没有安装APC或Memcached,我也没有为这个特定的项目使用任何框架

我希望页面被缓存(我认为默认情况下PHP“不允许”缓存)。到目前为止,我正在使用:

session_cache_limiter('private'); //Aim at 'public'
session_cache_expire(180);
header("Content-type: $documentMimeType; charset=$documentCharset");
header('Vary: Accept');
header("Content-language: $currentLanguage");
我读了很多教程,但找不到简单的东西(我知道缓存很复杂,但我只需要一些基本的东西)

“必须”有哪些头文件可以发送以帮助缓存?


<?php
header("Expires: Sat, 26 Jul 2020 05:00:00 GMT"); // Date in the future
?>

设置缓存页面的过期日期是将其缓存到客户端的一种有用方法。

选择或全部使用它们!:-)

页眉('Expires:Thu,01-Jan-70 00:00:01 GMT'); 标题('Last-Modified:'.gmdate('D,dm Y H:i:s')。'GMT'); 标头('Cache-Control:无存储,无缓存,必须重新验证'); 标头('Cache-Control:post-check=0,pre-check=0',false); 标题('Pragma:no cache');
您必须具有Expires标头。从技术上讲,还有其他解决方案,但Expires标头确实是最好的解决方案,因为它告诉浏览器在过期日期和时间之前不要重新检查页面,而只提供缓存中的内容。它真的很棒

在来自浏览器的请求中检查If-Modified-Since头也很有用。当浏览器“不确定”其缓存中的内容是否仍然是正确版本时,将发送此标题。如果您的页面从那时起没有被修改,只需发送回HTTP 304代码(未修改)。下面是一个发送304代码10分钟的示例:

<?php
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  if(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < time() - 600) {
    header('HTTP/1.1 304 Not Modified');
    exit;
  }
}
?>


您可以在代码的早期进行此检查以节省服务器资源。

您可能希望使用
private\u no\u expire
而不是
private
,但是,为您知道不会更改的内容设置一个较长的过期期限,并确保您处理自以来修改过的
,以及与Emil的帖子类似的
请求(如果没有匹配)

$tsstring = gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT';
$etag = $language . $timestamp;

$if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;
$if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
if ((($if_none_match && $if_none_match == $etag) || (!$if_none_match)) &&
    ($if_modified_since && $if_modified_since == $tsstring))
{
    header('HTTP/1.1 304 Not Modified');
    exit();
}
else
{
    header("Last-Modified: $tsstring");
    header("ETag: \"{$etag}\"");
}
其中,
$etag
可以是基于内容或用户ID、语言和时间戳的校验和,例如

$etag = md5($language . $timestamp);

我在服务器上做JSON缓存,来自Facebook提要。在我将flush和hid错误报告放在一起之前,一切都不正常。我知道这不是理想的代码,但我想快速修复

error_reporting(0);
    $headers = apache_request_headers();
    //print_r($headers);
    $timestamp = time();
    $tsstring = gmdate('D, d M Y H:i:s ', $timestamp) . 'GMT';
    $etag = md5($timestamp);
    header("Last-Modified: $tsstring");
    header("ETag: \"{$etag}\"");
    header('Expires: Thu, 01-Jan-70 00:00:01 GMT');

    if(isset($headers['If-Modified-Since'])) {
            //echo 'set modified header';
            if(intval(time()) - intval(strtotime($headers['IF-MODIFIED-SINCE'])) < 300) {
              header('HTTP/1.1 304 Not Modified');
              exit();
            }
    }
    flush();
//JSON OP HERE
错误报告(0);
$headers=apache_请求_头();
//打印(页眉);
$timestamp=time();
$tsstring=gmdate('D,dmy H:i:s',$timestamp)。'格林尼治标准时间';
$etag=md5($timestamp);
标题(“上次修改:$tsstring”);
标题(“ETag:\”{$ETag}\”;
页眉('Expires:Thu,01-Jan-70 00:00:01 GMT');
if(isset($headers['if-Modified-Since'])){
//echo“设置修改的标题”;
if(intval(time())-intval(strotime($headers['if-MODIFIED-SINCE']))<300){
标题(“HTTP/1.1 304未修改”);
退出();
}
}
冲洗();
//JSON操作在这里

这非常有效。

这里有一个为您提供http缓存的小类。它有一个名为“Init”的静态函数,需要2个参数,一个页面(或浏览器请求的任何其他文件)上次修改日期的时间戳,以及浏览器可以在缓存中保存此页面的最长时间(以秒为单位)

class HttpCache 
{
    public static function Init($lastModifiedTimestamp, $maxAge)
    {
        if (self::IsModifiedSince($lastModifiedTimestamp))
        {
            self::SetLastModifiedHeader($lastModifiedTimestamp, $maxAge);
        }
        else 
        {
            self::SetNotModifiedHeader($maxAge);
        }
    }

    private static function IsModifiedSince($lastModifiedTimestamp)
    {
        $allHeaders = getallheaders();

        if (array_key_exists("If-Modified-Since", $allHeaders))
        {
            $gmtSinceDate = $allHeaders["If-Modified-Since"];
            $sinceTimestamp = strtotime($gmtSinceDate);

            // Can the browser get it from the cache?
            if ($sinceTimestamp != false && $lastModifiedTimestamp <= $sinceTimestamp)
            {
                return false;
            }
        }

        return true;
    }

    private static function SetNotModifiedHeader($maxAge)
    {
        // Set headers
        header("HTTP/1.1 304 Not Modified", true);
        header("Cache-Control: public, max-age=$maxAge", true);
        die();
    }

    private static function SetLastModifiedHeader($lastModifiedTimestamp, $maxAge)
    {
        // Fetching the last modified time of the XML file
        $date = gmdate("D, j M Y H:i:s", $lastModifiedTimestamp)." GMT";

        // Set headers
        header("HTTP/1.1 200 OK", true);
        header("Cache-Control: public, max-age=$maxAge", true);
        header("Last-Modified: $date", true);
    }
}
类HttpCache
{
公共静态函数Init($lastModifiedTimestamp,$maxAge)
{
if(self::IsModifiedSince($lastModifiedTimestamp))
{
self::SetLastModifiedHeader($lastModifiedTimestamp,$maxAge);
}
其他的
{
self::SetNotModifiedHeader($maxAge);
}
}
私有静态函数IsModifiedSince($lastModifiedTimestamp)
{
$allHeaders=getallheaders();
if(数组\键\存在(“如果自”$allHeaders)修改)
{
$gmtSinceDate=$allHeaders[“如果自”]起修改);
$sinceTimestamp=strottime($gmtSinceDate);
//浏览器可以从缓存中获取它吗?

如果($sinceTimestamp!=false&&$lastModifiedTimestamp这是php缓存的最佳解决方案,只需在脚本顶部使用它即可

$seconds_to_cache = 3600;
$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
header("Expires: $ts");
header("Pragma: cache");
header("Cache-Control: max-age=$seconds_to_cache");

欢迎使用StackOverflow。第一个问题很好!如何指定.js和.css文件的缓存?或者它们是否包含在PHP文件生成的头中?@David Spector您可以使用Web服务器缓存它们。在Apache下,您可以使用.htaccess查找“头集缓存控制”来实现这一点。您可能想尝试问一个问题,因为评论不适合这样做:)会话缓存限制器和会话缓存过期已经控制过期,缓存控制,最后修改和Pragma…我明白了。是的,你是对的。我也看到我对你的问题读得不够好。很抱歉,使用会话缓存限制器和会话缓存过期已经解决了这个问题。你所描述的是一个弱电子标签和should有一个“W/”前缀。我会添加“Expires”标题,以及一些托管提供商(例如服务器)不管怎样发送它,他们都会在过去使用date,而我认为应该是在将来。如果你没有用上面的方法获得304s,请检查HTTP中是否有任何错误的引号。将
$If\u NONE\u MATCH==$etag
替换为
rtrim(ltrim($If\u NONE\u MATCH,“\”,“\”)==$etag
当然……不要忘记,理想的电子标签是基于您可以读取页面内容的事实计算出来的。在发送到浏览器之前,要发送到浏览器的HTML就是。通常它还意味着使用缓冲区。可能已经由现代php框架管理。如果您想发送“请不要缓存我”标题,最好使用缓存控件。为什么使用缓存控件而不是Pragma?因此,最好发送一个C
$seconds_to_cache = 3600;
$ts = gmdate("D, d M Y H:i:s", time() + $seconds_to_cache) . " GMT";
header("Expires: $ts");
header("Pragma: cache");
header("Cache-Control: max-age=$seconds_to_cache");