【问题标题】:Make PHP page return "304 Not Modified" if it hasn't been modified如果 PHP 页面没有被修改,则使 PHP 页面返回“304 Not Modified”
【发布时间】:2011-02-28 00:58:57
【问题描述】:

我有一个 PHP 文件,它每次都会使用相同的 $_GET 参数返回相同的东西——这是确定性的。

不幸的是,为了提高效率(这个文件经常被请求),每当请求 PHP 页面时,Apache 默认都会响应“200 OK”,从而使用户再次下载文件。

有没有办法发送304 Not Modified 标头当且仅当参数相同?

奖励: 我可以设置一个过期时间,这样如果缓存页面超过三天,它就会发送“200 OK”响应?

【问题讨论】:

    标签: php apache caching http-status-codes


    【解决方案1】:

    您是否在被调用的 PHP 代码中尝试过 header("HTTP/1.0 304 Not Modified");?如果不熟悉,您可以在开始向缓冲区输出任何内容之前将其放入代码中。

    http://php.net/manual/en/function.header.php

    【讨论】:

    • 每次访问脚本时都要调用我吗?
    【解决方案2】:

    通常,您使用 Header 函数返回 HTTP 状态代码:

    Header("HTTP/1.1 304 Not Modified");
    exit();
    

    但是,仅此还不够。

    问题是你不知道文件是如何请求的,所以你需要一点浏览器的配合。

    您可以在传入请求中查找 If-modified-since 标头,如果存在且在日期范围内,则返回相应的状态代码。

    如果您在最初生成 PHP 时发送了正确的 Expires 标头,则浏览器或代理缓存可能会决定根本不获取请求(尽管更有可能,它们会设置 If-modified-since 标头)。如果没有 Expires 标头,浏览器可能总是会重新尝试完整的请求。

    如需更多信息,请查看http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 并搜索“14.25”

    浏览器会将GET 参数映射到缓存副本,顺便说一句。你不需要在那里做任何工作。

    【讨论】:

      【解决方案3】:

      如果不自己缓存页面(或至少是其 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

      【讨论】:

      • 这对我来说似乎是一般的想法。
      • 虽然对于 OP 来说没有必要缓存任何东西。根据他的条件,他可以只根据id计算一个Etag。
      • 是的,实际的实现可以根据情况的需要简单或详细。试图说明这一点,但我陷入了提供实现的陷阱,md5()ing over content,不应该存在:P
      【解决方案4】:

      此脚本将再次渲染整个 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");
      

      【讨论】:

        猜你喜欢
        • 2023-02-12
        • 1970-01-01
        • 1970-01-01
        • 2011-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-22
        • 1970-01-01
        相关资源
        最近更新 更多