OpenResty通过ssl_certificate_by_lua*在TLS握手阶段按SNI动态加载PEM证书,无需reload;需满足版本≥1.19.3.1、启用SSL模块、禁用session缓存等条件,并配置fallback证书与安全读取机制。

OpenResty 支持动态加载 SSL 证书,核心不是靠 reload 配置,而是利用 ssl_certificate_by_lua* 在 TLS 握手阶段实时按 SNI 域名加载 PEM 格式的证书与私钥。这对管理成百上千子域名、多租户 SaaS 或泛域名服务至关重要——无需每次更新证书都重启或重载服务,真正实现静默更新。
确认运行环境是否就绪
动态证书加载不是默认开启的功能,需提前验证以下几点:
- OpenResty 版本 ≥ 1.19.3.1(推荐使用 1.21.x 或更高稳定版)
- 编译时已启用
--with-http_ssl_module和 OpenSSL 1.1.1+ 支持 - Nginx 配置中启用了
ssl_certificate_by_lua_file(注意不是_block形式,后者兼容性受限) - 禁用共享内存型 session 缓存:
ssl_session_cache off,避免旧会话复用过期证书
准备证书存储与安全读取
证书和私钥必须为标准 PEM 格式,且不能含密码(否则 Lua 无法直接加载)。建议采用以下方式组织:
- 将每个域名的
fullchain.pem和privkey.pem按域名命名,存于统一目录(如/etc/letsencrypt/live/) - 在 Lua 脚本中用
io.open()安全读取,并通过lua-resty-lrucache缓存解析结果,避免每次握手都触发磁盘 IO 和 PEM 解析开销 - 路径校验必不可少:只允许读取白名单目录下的文件,防止路径遍历攻击(例如拒绝
../../etc/shadow类请求)
配置 server 块与 fallback 机制
server 块需保留一个兜底证书(fallback),确保当 Lua 加载失败时仍能完成 TLS 握手,避免连接中断:
- 监听 443 端口,
server_name _匹配所有 SNI 域名 - 设置静态
ssl_certificate和ssl_certificate_key指向一个有效但低优先级的通配符或自签名证书 - 在该 block 内启用
ssl_certificate_by_lua_file /path/to/dynamic_ssl.lua - 脚本中根据
ngx.var.ssl_server_name获取 SNI 域名,查表/查缓存/读文件加载对应证书链与私钥
对接 Let’s Encrypt 自动续期流程
动态加载天然适配 ACME 自动化体系,关键在于让续期工具(如 Certbot)更新证书后,Lua 层能自动感知变化:
- 证书文件更新后,可触发 Lua 缓存失效(例如基于 mtime 判断),下一次握手即加载新内容
- 也可结合
lua_shared_dict记录各域名证书最后更新时间,在 Lua 中做轻量级热重载判断 - 无需修改 Nginx 配置、不调用
nginx -s reload,整个过程对线上流量零干扰


















