如何配置PHP缓存头以避免浏览器发起不必要的请求
解决浏览器频繁发起缓存验证请求(304)的问题
要让浏览器在缓存有效期内完全不发起请求,核心是强制浏览器信任本地缓存,不触发任何条件验证,你的代码里有几个关键点需要调整:
问题分析
你当前设置了Last-Modified和Etag头,这两个头会让浏览器在缓存有效期内仍可能发起条件请求(比如用户手动刷新页面、浏览器默认策略触发验证),服务器返回304就是这种条件请求的响应。同时缺少immutable属性,无法明确告诉浏览器资源不会变更。
修改方案
- 更新Cache-Control头:添加
immutable属性,明确告知浏览器在max-age有效期内,资源不会发生任何变化,无需发起验证请求;同时保留max-age设置缓存时长。 - 移除Last-Modified和Etag头:这两个头是触发条件请求的根源,删除后浏览器不会发送
If-Modified-Since或If-None-Match请求头。 - 删除304响应处理逻辑:既然浏览器不会发起条件请求,这部分代码就不再需要。
修改后的代码:
$location = base64_decode($_GET['location']); $path = path($location); header('X-Sendfile: ' . $path); header('Content-type: ' . mime_content_type($path)); header('Content-Disposition: inline; filename="' . basename($path) . '"'); // 调整Cache-Control,添加immutable属性 header('Cache-Control: private, max-age=' . 60 * 60 * 24 . ', immutable'); // 保留Expires作为兼容旧浏览器的 fallback header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 60 * 60 * 24) . ' GMT'); // 移除Last-Modified和Etag头 // header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', filemtime($path))); // header('Etag: "' . md5_file($path) . '"'); // 删除304响应处理逻辑 // if ((isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($path)) || // (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) // ) { // header('HTTP/1.1 304'); // exit(); // } readfile($path);
补充说明
immutable属性支持所有现代浏览器(Chrome、Firefox、Edge等),旧浏览器会忽略这个属性,仍可能发起条件请求,但这是兼容权衡。- 如果资源未来可能会更新,建议设置更长的max-age(比如30天),同时使用文件名哈希(比如
image_v1.jpg)来强制浏览器获取新资源,而不是依赖缓存验证。 - 测试时要确保浏览器没有开启"禁用缓存"的调试模式,否则所有设置都会被忽略。
内容的提问来源于stack exchange,提问作者Rick007110
相关产品推荐
相关产品推荐

