为何浏览器不缓存PHP readfile()输出的文件?
问题分析与解决方案
首先看你的PHP输出的响应头,里面有两个致命的缓存抑制头:
Expires: Thu, 19 Nov 1981 08:52:00 GMTPragma: no-cache
这两个是HTTP/1.0时代的缓存控制字段,它们的优先级会覆盖你设置的Cache-Control: public, max-age=3600,直接告诉浏览器“不要缓存这个资源”——这就是浏览器不缓存的核心原因!
这两个头通常是PHP默认添加的,比如当你开启了session但没有配置session.cache_limiter时,PHP会自动输出这些头来防止缓存会话相关内容。
解决方案步骤
移除有害的缓存抑制头
在输出自定义缓存头之前,先清理掉PHP默认添加的这些头:// 禁用PHP默认的缓存限制器 session_cache_limiter('none'); // 或者直接移除已存在的Expires和Pragma头 header_remove('Expires'); header_remove('Pragma');修正Etag格式(可选但推荐)
你的PHP输出的Etag没有引号,而Nginx的是带双引号的(符合HTTP规范),统一格式可以避免潜在的缓存验证问题:header("Etag: \"" . md5_file($fp) . "\"");添加Accept-Ranges头(可选)
和Nginx保持一致,添加上这个头可以支持断点续传,提升用户体验:header("Accept-Ranges: bytes");确保输出流程正确
你用了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
相关产品推荐
相关产品推荐

