本质是fastcgi_cache未区分用户上下文导致验证码图片空白和session错乱,必须在nginx中对/captcha等动态接口显式禁用缓存,并设置vary: cookie头或增强cache_key以隔离用户。

开启 fastcgi_cache 后验证码图片空白、Session 值错乱(比如 A 用户看到 B 用户的验证码、提交时提示“验证码错误”),本质是缓存未区分用户上下文,把本该私有的 Session 数据或动态图像响应给错误地缓存并复用了。这不是验证码本身写错了,而是缓存策略越界了。
关键:禁止缓存含 Session 或动态图像的请求
fastcgi_cache 默认对所有 200 响应一视同仁,而验证码接口(如 /captcha)返回的是 PNG 图片 + 设置 Cookie/Session 的响应,一旦被缓存,后续用户请求就会直接拿到旧图片和旧 session_id,造成“串视”。必须在 Nginx 配置中显式排除这类路径。
- 在 location 块中对验证码入口加
fastcgi_cache_bypass 1;和fastcgi_no_cache 1; - 示例配置:
location ^~ /captcha { fastcgi_cache_bypass 1; fastcgi_no_cache 1; include fastcgi_params; fastcgi_pass php_backend; } - 同理,所有涉及 Session 写入的登录、校验、表单提交接口(如
/login,/check-captcha)也需同样处理,不能只拦图片路径
检查 Vary 响应头是否缺失
即使没全禁缓存,也要确保后端在返回验证码图片时带上 Vary: Cookie(或 Vary: X-Forwarded-For, Cookie)。这个头告诉 Nginx:“这个响应依赖 Cookie,请按 Cookie 值分 cache key”。否则所有用户共用一个缓存条目。
- ThinkPHP 可在验证码方法开头加:
header('Vary: Cookie'); - PHPCMS 或自定义脚本中,在输出图片前手动设置该头
- Nginx 本身不自动添加 Vary,必须由 PHP 脚本输出
确认缓存 key 是否包含用户标识
如果仍想对部分动态内容做缓存(比如带用户昵称的页面片段),需主动构造带区分度的 cache key:
- 在
fastcgi_cache_key中加入$cookie_PHPSESSID或$cookie_session_id - 例如:
fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_PHPSESSID"; - 注意:若用 Redis 存 Session,确保 Cookie 名与实际一致(如 ThinkPHP 可能用
thinkphp_session)
清理已有污染缓存并验证
旧缓存可能已混杂多用户数据,上线新规则后必须清空:
- 执行
rm -rf /path/to/fastcgi_cache/*(对应fastcgi_cache_path设置的目录) - 重启 Nginx 或发送
kill -USR1 $(cat /var/run/nginx.pid)重载缓存索引 - 用不同浏览器或隐身窗口访问验证码页,检查响应头是否有
X-FastCGI-Cache: MISS,连续两次请求应为HIT才说明缓存生效且隔离正常











