You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何浏览器不缓存PHP readfile()输出的文件?

问题分析与解决方案

首先看你的PHP输出的响应头,里面有两个致命的缓存抑制头:

Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache

这两个是HTTP/1.0时代的缓存控制字段,它们的优先级会覆盖你设置的Cache-Control: public, max-age=3600,直接告诉浏览器“不要缓存这个资源”——这就是浏览器不缓存的核心原因!

这两个头通常是PHP默认添加的,比如当你开启了session但没有配置session.cache_limiter时,PHP会自动输出这些头来防止缓存会话相关内容。

解决方案步骤

  1. 移除有害的缓存抑制头
    在输出自定义缓存头之前,先清理掉PHP默认添加的这些头:

    // 禁用PHP默认的缓存限制器
    session_cache_limiter('none');
    // 或者直接移除已存在的Expires和Pragma头
    header_remove('Expires');
    header_remove('Pragma');
    
  2. 修正Etag格式(可选但推荐)
    你的PHP输出的Etag没有引号,而Nginx的是带双引号的(符合HTTP规范),统一格式可以避免潜在的缓存验证问题:

    header("Etag: \"" . md5_file($fp) . "\"");
    
  3. 添加Accept-Ranges头(可选)
    和Nginx保持一致,添加上这个头可以支持断点续传,提升用户体验:

    header("Accept-Ranges: bytes");
    
  4. 确保输出流程正确
    你用了ob_start(),记得在readfile后正确结束输出缓冲,避免内容截断:

    readfile($fp);
    ob_end_flush();
    exit;
    

修改后的完整代码示例

ob_start();
outputfile($fp);

function outputfile($fp) {
    // 先禁用默认缓存限制,移除有害头
    session_cache_limiter('none');
    header_remove('Expires');
    header_remove('Pragma');
    
    // 输出标准缓存相关头
    header("Content-Type: $mime_type");
    header("Content-Length: " . filesize($fp));
    header("Cache-Control: public, max-age=3600");
    header("Etag: \"" . md5_file($fp) . "\"");
    $date = gmdate("D, j M Y H:i:s", filemtime($fp)) . " GMT";
    header("Last-Modified: $date");
    header("Accept-Ranges: bytes");
    
    // 输出文件并结束缓冲
    readfile($fp);
    ob_end_flush();
    exit;
}

验证修改后的响应头

修改后,你的响应头应该不再包含Expires(或者设置为未来的时间)和Pragma: no-cache,浏览器就会遵循Cache-Control的规则缓存图片资源了。

内容的提问来源于stack exchange,提问作者Ken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:04:16