caddy 的 timeout 会终止长任务,因其默认30秒限制整个http请求生命周期;需在caddyfile的reverse_proxy块中显式配置timeout(如300s)并调整keepalive参数。

不会自动打断,但默认配置下可能被 Caddy 层的超时机制终止。FrankenPHP 本身不设 PHP 请求级超时,它的行为完全继承自 Caddy 的 HTTP 超时策略,而 Caddy 默认对请求整体(包括后端处理)施加了 timeout 限制。
为什么 Caddy 的 timeout 会干掉你的长任务
Caddy 的 timeout 是针对整个 HTTP 请求生命周期的,从接收第一个字节到返回最后一个字节。如果你在 index.php 里写了个耗时 60 秒的 sleep(60) 或数据库导出逻辑,而 Caddy 的 timeout 是默认的 30 秒,那连接会在 30 秒后被主动关闭——此时 PHP 进程其实还在跑,但客户端已断开,响应无法送达。
常见现象包括:502 Bad Gateway、net::ERR_CONNECTION_RESET、Caddy access log 中出现 timeout 字样。
- Caddy 的
timeout配置项位于reverse_proxy块或顶层handle中,FrankenPHP 的 PHP 处理器本身不暴露独立的“PHP 执行超时”开关 - PHP 自身的
max_execution_time仍生效,但它只影响脚本内部逻辑;若 Caddy 先断连,PHP 就收不到中断信号,可能继续执行但结果丢弃 - worker 模式下,长任务不会阻塞其他请求(这是 FrankenPHP 的优势),但单个请求仍受 Caddy 网络层超时约束
怎么调?重点改 Caddyfile 里的 reverse_proxy timeout
你得显式延长 Caddy 的代理超时,而不是去动 php.ini。在 Caddyfile 对应站点配置中,把 reverse_proxy 块补全并加大 timeout:
reverse_proxy * {
transport http {
keepalive 30s
keepalive_idle 30s
}
timeout 300s # ← 关键:从默认 30s 改成你需要的秒数,比如 300(5 分钟)
}
注意:timeout 必须写在 reverse_proxy 块内,不能放在顶层;FrankenPHP 的 php 指令本身不接受超时参数。
- 如果用的是
php指令直连(如php /var/www),它底层仍走 Caddy 的 reverse_proxy 逻辑,所以同样要配timeout - 别漏掉
keepalive相关参数,否则长连接中途可能被中间设备(如云厂商 LB)静默断开 - 生产环境建议配合前端加 loading 状态 + 后端返回 task_id,用轮询或 Mercure 推送替代纯同步长请求
worker 模式下还要注意进程保活和内存泄漏
启用 worker 模式(frankenphp_worker)后,PHP 进程常驻,长任务不会导致进程重启,但隐患转向另一侧:
- 没手动
gc_collect_cycles()或 unset 大变量,一次长任务可能吃光内存,后续请求直接 OOM - 数据库连接未设置
PDO::ATTR_PERSISTENT => false,长任务期间连接可能被服务端 kill,后续查询报MySQL server has gone away - worker 进程内没有心跳或健康检查机制,一旦卡死,Caddy 不会自动拉起新 worker——得靠
frankenphp_max_requests主动轮换
真正容易被忽略的是:Caddy 的 timeout 和 PHP 的 max_execution_time 是两套独立机制,前者管网络通道,后者管脚本 CPU 时间。你在 worker 里调 set_time_limit(0) 只能绕过后者,对前者完全无效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











