PHP 动态生成图片(如缩略图、水印图)直接输出时,缺乏 HTTP 缓存协商头,导致 CDN 每次回源,后端压力剧增。本文给出标准 ETag/Last-Modified + 304 Not Modified 完整实现,让动态图片同样享受 CDN/浏览器强缓存与协商缓存。
image 为图片二进制内容或文件路径
$etag = md5($this->image); // 基于内容生成 ETag
// 1. 协商缓存:检查 If-None-Match (ETag)
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$client_etag = $_SERVER['HTTP_IF_NONE_MATCH'];
if ($client_etag === $etag) {
header("HTTP/1.1 304 Not Modified");
exit;
}
}
// 2. 协商缓存:检查 If-Modified-Since (Last-Modified)
// $last_modified = gmdate("D, d M Y H:i:s", filemtime($image_path)) . " GMT";
// if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { ... }
// 3. 发送缓存控制头
header("Last-Modified: " . gmdate("D, d M Y H:i:s", strtotime('2011-1-1')) . " GMT");
header('ETag: ' . $etag);
header('Cache-Control: max-age=2592000, public'); // 30 天强缓存
// 4. 输出图片
echo $this->image;
?>
200 OK + ETag + Last-Modified + Cache-Control。CDN/浏览器缓存响应体与头。If-None-Match: <etag> 和/或 If-Modified-Since: <date>。304 Not Modified(空响应体),客户端直接读缓存;不一致 → 200 OK 返回新内容。$_SERVER['HTTP_*'](大写、下划线、减号转下划线)。无需在 fastcgi_param 重复配置。若缺失,检查 fastcgi_pass_header 或 proxy_pass_header 设置。md5_file() 或 sha1_file() 避免全量读入内存;或用 filemtime()+filesize() 组合作为弱 ETag。ETag 一致(用内容哈希而非 inode)。public 允许 CDN 缓存,max-age 控制强缓存时间,must-revalidate 过期后必须回源验证。location ~* \.(jpg|jpeg|png|gif|webp)$ {
# PHP 动态图片路由已处理缓存头,此处仅作兜底
expires 30d;
add_header Cache-Control "public";
# 关闭 Nginx 自身 ETag 避免冲突(可选)
# etag off;
}
补全 HTTP 缓存协商头,动态图片瞬间具备静态资源级 CDN 加速能力,后端压力降 90%+。