【问题标题】:How to use HTTP cache headers with PHP如何在 PHP 中使用 HTTP 缓存标头
【发布时间】:2009-12-28 21:43:36
【问题描述】:

我有一个 PHP 5.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");

我阅读了很多教程,但找不到简单的东西(我知道缓存很复杂,但我只需要一些基本的东西)。

什么是“必须”发送标头以帮助缓存?

【问题讨论】:

  • 欢迎来到 StackOverflow。第一个问题很好!
  • 如何为 .js 和 .css 文件指定缓存?或者它们是否包含在 PHP 文件生成的标头中?
  • @David Spector 你会用你的网络服务器缓存它们。在 Apache 下,您可以通过 .htaccess 查找“Header set Cache-Control”来执行此操作。您可能想尝试提出问题,因为 cmets 不适合这样做:)

标签: php http caching


【解决方案1】:

您可能想使用private_no_expire 而不是private,但为您知道不会更改的内容设置一个较长的过期时间,并确保您处理if-modified-since 和if-none-match 的请求,类似于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);

【讨论】:

  • 您所描述的将是一个弱电子标签,应该有一个“W/”前缀。
  • 我也会添加“Expires”标头,因为一些托管服务提供商(例如服务器)无论如何都会发送它,并且他们使用过去的日期,而我认为它应该在未来。
  • 如果您在上面没有得到 304,请检查 HTTP_IF_NONE_MATCH 中是否有任何杂散引号。确定将$if_none_match == $etag 替换为rtrim(ltrim($if_none_match, "'\""), "'\"") == $etag。
  • ...不要忘记,理想的电子标签是根据您可以阅读页面内容的事实来计算的。要发送到浏览器的 HTML,在发送到浏览器之前。一般来说,它也意味着使用缓冲区。可能已经由现代 php 框架管理。如果您想发送“请不要缓存我”标头,最好使用 Cache-Control。为什么选择 Cache-Control 而不是 Pragma? Because Pragma is deprecated。因此,最好发送一个 Cache-Control 标头。点下来给误导。不错的尝试,不过:)
  • $timestamp 来自哪里?是否应该在代码的其余部分 $timestamp = time(); 之前定义它?我错过了什么吗?
【解决方案2】:

您必须有一个 Expires 标头。从技术上讲,还有其他解决方案,但 Expires 标头确实是最好的解决方案,因为它告诉浏览器不要在到期日期和时间之前重新检查页面,而只是从缓存中提供内容。真的很棒!

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

<?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;
  }
}
?>

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

【讨论】:

    【解决方案3】:

    任您选择 - 或全部使用! :-)

    header('过期时间:星期四,01-Jan-70 00:00:01 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache');

    【讨论】:

    • session_cache_limiter 和 session_cache_expire 已经控制了 Expires、Cache-Control、Last-Modified 和 Pragma...
    • 我明白了。是的你是对的。而且我还看到我没有很好地阅读您的问题。对不起
    【解决方案4】:
    <?php
    header("Expires: Sat, 26 Jul 2020 05:00:00 GMT"); // Date in the future
    ?>
    

    为缓存页面设置过期日期是在客户端缓存它的一种有用方法。

    【讨论】:

    • 很好,使用 session_cache_limiter 和 session_cache_expire 已经解决了这个问题。
    【解决方案5】:

    这是一个为您进行 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);
        }
    }
    

    【讨论】:

      【解决方案6】:

      这是 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");
      

      【讨论】:

      • 这真的有助于解释你的答案,以便其他人知道为什么这是最好的解决方案!
      • 将此代码添加到您的 PHP 页面顶部,它将强制将您的文件缓存到浏览器
      【解决方案7】:

      我在服务器上进行来自 Facebook 提要的 JSON 缓存,直到我输入刷新和隐藏错误报告之前,什么都没有工作。我知道这不是理想的代码,但想要快速修复。

      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
      

      效果很好。

      【讨论】:

      • 如果不将字段名 $headers['If-Modified-Since'] 设置为大写可能不起作用 -> $headers['IF-MODIFIED-SINCE]
      猜你喜欢
      • 1970-01-01
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 2011-09-01
      • 1970-01-01
      • 2012-08-26
      • 1970-01-01
      相关资源
      最近更新 更多