frankenphp不接管symfony缓存逻辑,调优关键是确保其caddy层不破坏symfony原生缓存链路:禁用caddy cache指令、避免header硬覆盖、分离php与reverse_proxy路由、禁用fastcgi自动重写,并用symfony/http-client保障esi缓存一致性。

FrankenPHP 本身不接管 Symfony 的 HTTP 缓存逻辑(如 Response::setPublic()、Cache-Control 头、ESI),但它的 Caddy 层会严格透传或干扰这些头——尤其在开启 worker 模式后,响应生命周期变长,缓存行为容易出偏移。调优关键不是“让 FrankenPHP 支持缓存”,而是确保它不破坏 Symfony 原生的缓存链路。
为什么 Symfony 的 Response::setSharedMaxAge() 在 FrankenPHP 下失效
Symfony 默认用 Response::setSharedMaxAge() 设置 Cache-Control: public, s-maxage=3600,这依赖反向代理(如 Varnish)或 CDN 尊重 s-maxage。但 FrankenPHP 的 Caddy 层默认不识别 s-maxage,也不会自动降级为 max-age;更糟的是,Caddy 若启用了内置缓存(cache 指令),会直接忽略 PHP 输出的缓存头,自行决定是否缓存。
- 检查是否误启了 Caddy 的全局缓存:Caddyfile 中不能出现
cache块,除非你明确想覆盖 Symfony 控制权 - 确认
Response对象在发送前已调用setSharedMaxAge(),且未被中间件(如SurrogateMiddleware)提前修改 - 用
curl -I https://yoursite.com/path验证响应头:若看到Cache-Control缺失或被改写成no-cache,大概率是 Caddy 的header指令或php处理器隐式重置了头
php 指令里的 env 和 header 参数会覆盖 Symfony 缓存头
FrankenPHP 的 Caddyfile 中,php 指令默认会重置部分响应头(比如强制 Content-Type),而显式写的 header 子指令(如 header Cache-Control "no-store")会无条件覆盖 PHP 输出的所有缓存头。
- 删掉所有对
Cache-Control、Expires、Vary的硬编码header设置 - 如果必须加安全头(如
X-Content-Type-Options),用header -Cache-Control先清除旧头,再用header +Cache-Control追加,而非覆盖 -
env参数里避免设SYMFONY_ENV=prod以外的值——开发环境会触发 Symfony 自动插入Cache-Control: no-cache, private
Worker 模式下 ESI 片段缓存需手动启用 symfony/http-client 替代内置 cURL
Symfony 的 ESI(Edge Side Includes)依赖外部 HTTP 客户端发起子请求,而 FrankenPHP 的 worker 进程常驻内存后,PHP 内置的 curl 扩展默认复用连接池,可能把上一个请求的缓存头带到下一个 ESI 请求中,导致片段缓存失效或 404。
- 禁用
curl的连接复用:在php.ini中设curl.cainfo=/etc/ssl/certs/ca-certificates.crt并加curl_setopt($ch, CURLOPT_FORBID_REUSE, true)(不推荐,影响性能) - 更稳妥的做法:用
symfony/http-client替代原生 cURL,在config/packages/framework.yaml中配置:framework: http_client: default_options: max_redirects: 0 timeout: 5 - 确保 ESI 响应也带正确缓存头:在子控制器里显式调用
$response->setSharedMaxAge(300),不能只靠主响应头
Caddy 的 reverse_proxy 与 php 指令混用时缓存策略冲突
有人想用 FrankenPHP 处理 PHP,又用 Caddy 的 reverse_proxy 把部分路径转发给其他服务(如 API 网关),这时若两个指令共用同一 route 块,Caddy 可能对 PHP 路径也应用 reverse_proxy 的缓存策略(如 cache {}),完全绕过 Symfony 的逻辑。
- 严格分离路由:PHP 路径用
php指令,API 路径用独立reverse_proxy块,不要放在同一个handle里 - 检查 Caddy 日志:启动时加
-log stdout,搜索cache miss或cached response关键字,确认缓存动作是否发生在预期位置 - 线上环境禁用 Caddy 的
cache插件——FrankenPHP 不是 Varnish,它的缓存能力仅适合静态资源,不适合动态内容
最易被忽略的一点:FrankenPHP 的 php 指令默认开启 fastcgi 兼容模式,会自动添加 Status 头并重写 Content-Length,这在某些 Symfony 缓存中间件里会触发响应体重写,导致 ETag 或 Last-Modified 失效。如果你依赖这些头做协商缓存,必须在 Caddyfile 中显式关闭:php { env PHP_VALUE "output_buffering=Off" },并确保 Symfony 的 Response 已调用 setEtag() 或 setLastModified()。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











