varnish 能显著降低 symfony 页面响应时间,但默认配置下几乎不缓存任何内容——因为 symfony 默认返回 cache-control: private 和带 set-cookie 的响应,而 varnish 会直接 bypass 这类响应。

直接说结论:Varnish 能显著降低 Symfony 页面响应时间,但默认配置下几乎不缓存任何内容——因为 Symfony 默认返回 Cache-Control: private 和带 Set-Cookie 的响应,而 Varnish 会直接 bypass 这类响应。
为什么 Symfony 响应默认不被 Varnish 缓存
Varnish 对缓存非常保守:只要响应头里有 Set-Cookie、Cache-Control: private、no-cache 或 Authorization 请求头,它就拒绝缓存。Symfony 的 Response 对象在未显式设置缓存策略时,会自动添加 Cache-Control: no-cache, private,并可能在用户登录后写入 PHPSESSID 或自定义 session cookie。
- 即使你只访问一个公开页面(如
/about),只要控制器没调用$response->setPublic()或$response->headers->remove('Cache-Control'),Varnish 就不会缓存 - 登录态路由(如
/admin)必须排除缓存,但错误地让整个应用都带上Set-Cookie是常见原因 - 开发环境(
app_dev.php或APP_ENV=dev)会强制禁用缓存,切勿在 dev 下测试 Varnish 行为
如何让 Symfony 响应可被 Varnish 缓存
关键不是改 VCL,而是从 Symfony 层控制响应头。Varnish 只是执行者,它信任后端返回的缓存指令。
- 在控制器中显式声明缓存策略:
$response = new Response($content); $response->setPublic(); $response->setMaxAge(3600); $response->setSharedMaxAge(3600);
- 对完全静态的公开页面(如 CMS 文章页),推荐用
ResponseCache注解(Symfony 6.4+)或@Cache(旧版):#[Cache(public: true, maxage: 3600)]
- 确保 session 不在公共路由中启动:避免在未登录用户也能访问的控制器里调用
$this->getUser()或$request->getSession(),否则会触发Set-Cookie - 检查中间件或监听器是否偷偷加了
Cache-Control: no-store,比如某些安全 bundle 或调试工具
Varnish 配置里最该改的三处
不要一上来就重写整套 VCL;先确认 Symfony 已输出正确响应头,再微调 Varnish 行为。
- 去掉对
Cookie的粗暴清除:别在 VCL 里写unset req.http.cookie;,这会干掉登录态所需的认证 cookie。应该只清理无关 cookie:if (req.url ~ "\.(css|js|png|jpg|gif)$") { unset req.http.cookie; } - 允许缓存带
Vary: Accept-Encoding的响应(这是 Symfony 默认加的),在vcl_backend_response中保留它:if (beresp.http.Vary) { set beresp.http.Vary = regsub(beresp.http.Vary, "Accept-Encoding", "Accept-Encoding"); } - 启用
beresp.ttl显式继承:Varnish 默认只信任Cache-Control: max-age,但 Symfony 有时只设Expires。加一句:if (beresp.ttl > 0s) { set beresp.ttl = std.duration(beresp.ttl + "s", 120s); }
验证缓存是否真正生效
别只看浏览器 Network 面板——它显示的是客户端缓存。要确认 Varnish 是否命中,必须看响应头里的 X-Cache 或 X-Varnish 字段。
- 用
curl -I http://your-site.com/查看响应头,确认有X-Cache: HIT或X-Cache: MISS - 如果始终是
MISS,运行varnishlog -g request -q 'ReqURL ~ "^/"' | grep "BerespStatus\|BerespReason\|BerespHeader",重点看BerespStatus是否为 200,以及BerespHeader里有没有Cache-Control和Set-Cookie - 注意:本地开发用
localhost测试时,Varnish 默认不缓存localhost域名下的请求,务必用真实域名(如app.local)配合 hosts 修改
最容易被忽略的一点:Varnish 不会“修复”错误的缓存语义。如果你让一个用户专属页面(比如 /profile)返回了 public 缓存头,它真会被所有人看到——这不是 Varnish 的 bug,是你没理解 HTTP 缓存模型。缓存策略必须由业务逻辑层严格控制,Varnish 只负责忠实执行。











