根本原因是Caddy或中间设备(如SLB)主动关闭空闲TCP连接,非PHP代码卡住;需在Caddyfile中显式配置reverse_proxy transport http { keepalive 25s },开启debug日志搜索connection closed等字段定位中断环节。

FrankenPHP 1.2 长连接超时断开,根本原因不在 PHP 代码卡住,而是 Caddy 或中间网络设备(如 SLB、云防火墙)主动关闭了空闲 TCP 连接。解决的关键是显式配置连接保活策略,并准确定位中断环节。
检查并配置 Caddy 的 keepalive 参数
Caddy 默认不开启长连接复用,FrankenPHP 1.2 也不会自动适配。必须在 Caddyfile 的 reverse_proxy 块中手动添加 transport 配置:
- 在 reverse_proxy 区块内加入:
transport http { keepalive 25s }—— 这个值要小于上游设备(如阿里云 SLB 默认 60 秒)的空闲超时,留出缓冲 - 确保 reverse_proxy 外层有:
header_up Connection keep-alive和header_up X-Forwarded-Proto {scheme},否则下游可能无法复用连接 - 禁用任何 fastcgi 相关指令,FrankenPHP 走的是 HTTP 协议,混用会导致连接语义错乱
确认超时源头是否在中间代理层
很多情况下,Caddy 日志里看不到异常,但请求仍被重置。这时要排查是否被前置代理截断:
- 用
curl -v https://your-site.com查看响应头中的Server字段:若显示 nginx、envoy 或其他非 Caddy,说明前面还有反向代理,超时大概率由它触发 - 检查 SLB/CLB 控制台,确认其 TCP 空闲超时设置(常见为 60 秒),并将 Caddy 的 keepalive 设为比它小 10–15 秒
- 若使用宝塔面板,注意它默认在 Nginx 层做了转发,此时 FrankenPHP 实际运行在 Nginx 后方,需同步调整 Nginx 的
keepalive_timeout和proxy_http_version 1.1等配置
开启 Caddy debug 日志精准定位
FrankenPHP 自身不记录连接层日志,真正掌握连接生死的是 Caddy。启用详细日志才能看到真实原因:
立即学习“PHP免费学习笔记(深入)”;
- 在 Caddyfile 的全局或 log 指令块中加入:
log { level debug },然后重启 - 重点搜索日志中的关键词:
connection closed、reset、idle timeout、upstream.keepalive、close_idle_conns - 不要依赖 php-error.log —— 这些字段只出现在 Caddy 的 error 或 access 日志里
验证 HTTP/2 与连接复用是否生效
FrankenPHP 1.2 基于 HTTP/2 长连接模型,但需确认协议和头部是否正确协商:
- 用浏览器开发者工具或 curl -I 查看响应头是否有
connection: keep-alive和http2协议标识 - 若响应头出现
connection: close,说明某一层(Caddy、前置代理或客户端)禁用了 keep-alive,需回溯配置 - 可临时在 Caddyfile 中加
debug指令,观察启动时是否报 “HTTP/2 enabled” 或 “ALPN not negotiated” 类提示



















