ERR_CONNECTION_RESET 表明 TCP 连接被对方主动 RST 中断,需聚焦 FrankenPHP 进程状态、HTTP/2 与反向代理兼容性、PHP 应用层连接控制、系统资源限制四方面逐层排查。

FrankenPHP 出现 ERR_CONNECTION_RESET,说明浏览器发起的 HTTP 请求在 TCP 层被强制中断(收到 RST 报文),不是超时或拒绝连接,而是“正在传数据时被突然掐断”。这通常指向 FrankenPHP 进程、底层 PHP-FPM 行为、或其与 Caddy/Nginx 的协作链路出了问题。排查需聚焦在 FrankenPHP 自身运行状态、连接复用机制和资源管控上。
检查 FrankenPHP 是否健康运行且未崩溃
FrankenPHP 启动后若因 panic、内存溢出或扩展冲突意外退出,Caddy 或 Nginx 仍可能维持上游连接池,后续请求会发给已失效的进程,触发 RST。
- 查看 FrankenPHP 日志:启动时加
-v参数(如frankenphp serve -v),或检查其 stdout/stderr 重定向的日志文件,重点搜panic、fatal error、segmentation fault - 确认进程存活:
ps aux | grep frankenphp,看主进程和 worker 是否持续存在;用curl -s http://127.0.0.1:8080/health(若启用了内置健康端点)验证响应 - 临时改用 CLI 模式直连测试:
frankenphp run index.php,绕过反向代理,看是否仍报错——若此时正常,问题大概率出在反向代理配置或长连接转发逻辑中
验证反向代理对 HTTP/2 和 Keep-Alive 的兼容性
FrankenPHP 默认启用 HTTP/2 支持,但部分 Nginx 版本或 Caddy 配置若未正确透传或处理 h2 流,会在连接复用时产生脏状态,导致下游复用一个已被 FrankenPHP 关闭的流,触发 RST。
- Caddy 用户:确保
reverse_proxy块中启用了transport http并明确设置keep_alive 30s;禁用http2传输(加protocol http/1.1)做对比测试 - Nginx 用户:检查
upstream块是否配置了keepalive 32,并在location中添加proxy_http_version 1.1;和proxy_set_header Connection ''; - 用
curl -v --http2 https://yoursite.com和--http1.1分别测试,观察是否仅在 HTTP/2 下复现
排查 PHP 应用层主动关闭连接的行为
FrankenPHP 虽基于 SAPI,但仍继承 PHP 的连接控制逻辑。若应用代码中调用了 fastcgi_finish_request()、ignore_user_abort(true) 后又尝试写响应,或使用了不兼容的扩展(如某些旧版 xdebug、pcov),都可能导致连接状态混乱。
立即学习“PHP免费学习笔记(深入)”;
- 临时注释掉所有
fastcgi_finish_request()调用,以及register_shutdown_function()中涉及输出的操作 - 在
php.ini中禁用非必要扩展:extension=opcache.so保留,其余如xdebug.so、blackfire.so全部注释,重启 FrankenPHP - 检查是否设置了过短的
max_execution_time或output_buffering = Off导致响应未完整写出即中断
检查系统级连接限制与资源耗尽
FrankenPHP 在高并发下若遭遇文件描述符(fd)不足、TCP TIME_WAIT 积压或 cgroup 内存限制,内核可能直接 RST 新建连接。
- 运行
cat /proc/$(pgrep frankenphp)/limits | grep "Max open files",确认 soft limit ≥ 65536;不够则在 systemd service 文件中加LimitNOFILE=65536 - 执行
ss -s查看当前 socket 状态,若TIME-WAIT超过 3 万,考虑调大net.ipv4.tcp_tw_reuse=1(仅限客户端场景)或优化连接复用 - 如果跑在 Docker 或 systemd 服务中,检查是否设置了
MemoryMax或LimitMEMLOCK,导致 PHP 进程被 OOM killer 杀死后残留半开连接



















