frankenphp证书轮换不阻塞请求且不会中断在途连接;它复用caddy的tls.manage模块,通过异步验证、动态getcertificate回调和tls会话密钥复用实现原子性热更新,支持原子mv替换或sighup增量重载。

FrankenPHP 证书轮换是否阻塞请求
不会中断在途请求。FrankenPHP 基于 Caddy,而 Caddy 的 TLS 证书热更新是原子性、无中断的:新证书加载完成前,旧证书持续服务;切换瞬间由 Go 的 net/http.Server 调用 srv.ServeTLS 的底层机制保证连接不被关闭。
Caddy 的证书热加载原理
FrankenPHP 内部复用 Caddy 的 tls.Manage 模块,它通过以下方式避免请求中断:
- 证书文件变更后,Caddy 在后台异步读取并验证新证书链,不触碰当前活跃的 TLS listener
- 验证通过后,调用
http.Server.TLSConfig.GetCertificate返回新证书,该回调在每次 TLS 握手时动态生效 - 已建立的 TLS 连接继续使用原有会话密钥,不受影响;新握手自动协商新证书
- 整个过程无需重启进程、不中断监听 socket、不丢弃 accept 队列中的连接
和 Nginx + njs 共享字典方案的关键区别
FrankenPHP 不依赖外部共享内存或 JS 脚本控制证书路径,因此没有 js_shared_dict_zone 那类需手动触发刷新或担心 TTL 过期的环节。它的证书管理是声明式的:
- 只要证书文件(
cert.pem和key.pem)被原子替换(如mv new.crt cert.pem && mv new.key key.pem),Caddy 就会在下次检查周期(默认 1 分钟)内自动加载 - 若需立即生效,可向 FrankenPHP 进程发送
SIGHUP—— 它会触发 Caddy 的配置重载,但仅 reload TLS 配置,不中断 HTTP 连接 - 注意:
SIGHUP不等同于传统 Nginx 的 reload:FrankenPHP 的 reload 是增量式、模块级的,不会重建 event loop 或关闭 listener
实际部署中容易被忽略的点
真正导致“看似中断”的往往不是证书轮换本身,而是配套操作踩坑:
- 用
cp覆盖证书文件而非原子mv,可能让 Caddy 读到截断或不完整的 PEM,触发临时 TLS 握手失败 - 证书链顺序错误(比如把中间 CA 放在根证书前面),Caddy 会静默跳过该证书,回退到旧证书,但客户端若只信任新链,会出现首次访问失败
- Let’s Encrypt ISRG Root X1 兼容性问题(如 Android 7.1.1 及更早设备)是客户端信任链缺失,和 FrankenPHP 是否轮换无关,但容易误判为“轮换后访问不了”
- 日志里看不到明显报错,因为 Caddy 默认只在证书加载失败时写
WARN级日志,需确保启动时加了--log-level=warn或更高
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











