根本原因是浏览器缓存了响应内容,需在输出图片前设置Cache-Control: no-store等禁用缓存头,并排查服务器缓存、304响应误用及header输出顺序错误等问题。

PHP生成图片时浏览器仍显示旧图
根本原因是浏览器缓存了响应内容,而非PHP代码没执行。即使你用 file_put_contents() 覆盖了磁盘上的图片文件,只要HTTP响应头未明确禁止缓存,浏览器大概率会直接从本地缓存加载旧版本。
常见现象包括:header("Content-Type: image/png") 后输出图片二进制,但前端 一直不更新;或用 imagepng() 动态生成后,F5刷新也无变化。
实操建议:
- 在输出图片前,强制禁用缓存:
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");header("Pragma: no-cache");header("Expires: 0"); - 避免只写
Cache-Control: no-cache—— 它允许协商缓存(如ETag),而no-store才真正禁止存储 - 若图片URL固定(如
thumb.php?id=123),可在URL后加时间戳参数(thumb.php?id=123&t=)绕过缓存,但这是权宜之计,非根本解
PHP脚本返回304 Not Modified却没走逻辑
当浏览器发送带 If-Modified-Since 或 If-None-Match 的请求,而PHP未做校验就直接返回304,会导致图片生成逻辑被跳过,用户看到的仍是上次缓存的内容。
立即学习“PHP免费学习笔记(深入)”;
这种情况多见于手动实现静态资源服务、或误用了框架/中间件的缓存判断逻辑。
实操建议:
- 检查代码中是否出现
header("HTTP/1.1 304 Not Modified")或类似响应,且该分支里没有exit或后续输出 - 动态图片不应依赖文件修改时间做条件响应——因为PHP每次都要重新生成,
filemtime()比较无意义 - 如确需支持条件GET,必须配合
ETag(例如基于图片内容MD5)并完整实现校验逻辑,否则干脆禁用所有条件头处理
Apache/Nginx把PHP图片响应当静态文件缓存了
Web服务器可能对 .php 后缀做了默认缓存配置,尤其当它检测到 Content-Type: image/* 且响应体为二进制时,会按静态资源策略缓存整个响应(包括状态码和body),导致PHP脚本实际未被执行。
典型表现:重启PHP-FPM后首次访问正常,之后无论怎么改PHP代码都返回旧图;curl -I 查看响应头含 X-Cache: HIT 或 Age 字段。
实操建议:
- Apache下检查是否有
mod_cache+mod_file_cache启用,并确认块中未意外启用CacheEnable - Nginx下重点排查
proxy_cache或fastcgi_cache配置,确保location ~ \.php$块中设置了:fastcgi_cache off;add_header Cache-Control "no-cache, no-store"; - 临时验证:停用所有缓存模块或配置,用
curl -v http://yoursite/gen.php看是否每次返回新内容
GD/Imagick生成图片后header顺序出错
PHP中调用 imagepng()、imagejpeg() 等函数前,若已输出过任何内容(包括空格、BOM、echo、警告),会导致headers already sent错误,进而使Content-Type等关键头失效,浏览器可能按text/html解析响应,或退化为下载行为——此时你以为“图片没刷新”,其实是根本没正确渲染。
这类问题在Windows编辑器保存UTF-8 BOM格式的PHP文件时高频发生。
实操建议:
- 用
headers_sent($file, $line)检查是否提前输出,定位到具体文件与行号 - 确保所有图片输出脚本以
开头,前面**无任何字符**(包括空格、换行、BOM) - 开发阶段开启
error_reporting(E_ALL); ini_set('display_errors', '1');,让警告暴露出来 - 不要在
imagepng()后再调用exit—— GD函数本身不自动终止脚本,但多余输出会破坏二进制流
最易被忽略的是:缓存控制头必须在任何输出之前发送,且Web服务器层的缓存开关优先级高于PHP代码。哪怕你写了十行 header(),只要Nginx fastcgi_cache开着,就全作废。











