PHP 静态文件返回 304 Not Modified 缓存技巧

叶初
叶初
发布时间:2026-10-14 | 5人浏览过
原创

当 PHP 动态输出图片等静态资源时,默认往往每次都向服务器发起完整请求并返回 200 状态,造成不必要的带宽消耗。通过引入 HTTP 缓存机制,利用 Last-Modified 和 Etag 头信息,可以在客户端发现资源未发生变化时返回 304 Not Modified,从而让浏览器直接使用本地缓存副本。

为什么需要返回 304?

在 Web 开发中,即使图片本身没有改变,如果服务器不告知客户端 "未修改",浏览器仍会重新下载整个文件。这对于高频访问的图片资源是极大的性能浪费。通过验证资源指纹(时间戳或哈希值),我们可以精确控制缓存行为。

实现原理与准备工作

HTTP/1.1 提供了两种标准的缓存验证机制:

  • If-Modified-Since / Last-Modified:基于时间的比较。服务器发送资源的最后修改时间,客户端下次请求携带该时间,若相同则返回 304。
  • If-None-Match / Etag:基于内容的唯一标识符(通常由 MD5 或 CRC32 生成)。只要文件内容微调,标识符就会改变。

为了最高效地使用这两个机制,我们需要获取两个关键数据:
1. 文件的最后修改时间戳(filemtime)。
2. 文件的唯一标识字符串(如 md5_file 生成的哈希值)。

具体操作步骤

第1步:构建 304 缓存验证函数

目的:封装逻辑,复用性强。
入口:被调用的 PHP 脚本或基类。
动作:解析请求头中的验证信息,并与当前文件属性比对。若匹配,发送 304 头并终止脚本。

复制代码
private function _addEtag($file) {
    // 1. 获取文件的最后修改时间和内容哈希值
    $last_modified_time = filemtime($file); 
    $etag = md5_file($file);
    
    // 2. 始终发送响应头,供首次请求的客户端缓存
    header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT"); 
    header("Etag: \"$etag\""); 
    
    // 3. 检查客户端是否携带了旧的验证信息
    // 注意:$_SERVER['HTTP_IF_MODIFIED_SINCE'] 可能包含毫秒,需转换处理
    if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || 
        @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
        // 4. 内容未变,发送 304 状态码并退出,不传输任何 Body
        header("HTTP/1.1 304 Not Modified"); 
        exit; 
    }
}

参数说明:
- $file: 待服务的静态文件路径。
- header("Etag: ..."): 注意标准规范中 Etag 值通常需要加双引号,上面代码中已加入以确保兼容最佳实践。

第2步:在输出资源前调用验证函数

目的:拦截不符合条件的请求。
入口:在你的图片处理或文件服务脚本的最开头。
动作:传入文件路径,函数内部完成计算。如果命中缓存,直接退出;否则,继续执行下方的图片输出逻辑。

示例调用场景:
假设你有一个 image.php 用于显示用户上传的头像。

复制代码
<?php
$imagePath = 'uploads/avatar/user_123.jpg';
if (file_exists($imagePath)) {
    // 先调用 304 检测
    $_addEtag = function($f) {
        // ... 此处粘贴上述 _addEtag 的代码逻辑
    };
    $_addEtag($imagePath);
    
    // 只有通过了上面的 exit(),才会执行到这里
    header('Content-Type: image/jpeg');
    echo file_get_contents($imagePath);
}
?>

完成判断

  1. 首次访问:浏览器接收带有 Last-Modified 和 Etag 的完整图片数据及 200 状态。
  2. 再次访问(无更改):浏览器自动携带 If-Modified-Since 和 If-None-Match 请求头。服务器判定一致后,返回空正文且仅含 304 状态的响应。
  3. 查看效果:打开浏览器开发者工具(F12)→ Network(网络)面板,刷新页面。观察该图片请求的状态码是否为 304,且 Size 列为 (disk cache) 或 (memory cache)。

简要总结

通过在 PHP 层集成基于 filemtime 和 md5_file 的验证逻辑,可以有效复用浏览器的 HTTP 缓存能力。这不仅减少了服务器负载,也降低了用户的等待时间。以上就是利用 PHP Etag 和 Last-Modified 实现 304 缓存优化的详细内容,更多关于 PHP 性能优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表