frankenphp离线首次启动需禁用acme并预置本地证书:用openssl生成localhost.crt/.key放入certs/目录,caddyfile中显式指定tls路径,启动时加--http3-port=0关闭quic,避免隐式联网。

FrankenPHP单文件如何脱离网络完成首次启动
FrankenPHP的独立二进制默认依赖Caddy的自动HTTPS机制,而该机制在首次启动时会尝试连接Let’s Encrypt的ACME服务器(哪怕只用于localhost),导致离线环境下卡在证书申请环节、报错acme: error: 403 :: urn:ietf:params:acme:error:rateLimited或直接超时 hang 住。这不是bug,是Caddy默认行为——它不区分“是否真要对外提供HTTPS”,只要启用了TLS就走ACME流程。
Caddyfile里必须禁用ACME并硬编码本地证书
离线激活的核心是绕过ACME,改用预生成的自签名证书,并在Caddyfile中显式指定。不能只靠tls internal(它仍会尝试联网初始化)。
- 先用OpenSSL生成一对离线可用的证书(仅需一次,在有网机器上运行):
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \ -keyout localhost.key -out localhost.crt \ -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"
- 把
localhost.crt和localhost.key放到应用目录的certs/子目录下 -
Caddyfile中对应站点块写成:localhost { root * public php_server tls certs/localhost.crt certs/localhost.key }注意:不要写tls internal,不要留空tls,路径必须是相对frankenphp启动目录的路径
启动命令要加--no-https或屏蔽HTTP/3 UDP端口
即使配置了本地证书,FrankenPHP默认仍会监听UDP 443(HTTP/3)并尝试启用QUIC,而QUIC初始化可能触发额外的网络探测。稳妥做法是显式关闭不需要的协议。
- 如果只跑HTTP+HTTPS(不强制HTTP/3),启动时加:
./frankenphp --no-https
——这会禁用所有TLS相关逻辑,包括证书加载和HTTP/3,只留HTTP 80端口(适合纯内网调试) - 如果必须用HTTPS但离线,改用:
./frankenphp --http-port=80 --https-port=443 --http3-port=0
,其中--http3-port=0明确关闭UDP监听,避免QUIC层的隐式联网行为 - 验证是否生效:启动后
netstat -tuln | grep :443应只看到TCP监听,无UDP条目
嵌入式二进制打包时证书路径必须静态化
当你用EMBED=dist/app/ ./build-static.sh打包成单文件时,Caddyfile里写的certs/localhost.crt路径会被固化进二进制。这意味着证书文件必须在打包前就放在dist/app/certs/下,且运行时**不能**通过挂载或环境变量动态替换——嵌入模式下Caddy不读取外部文件系统,只从打包进来的FS镜像里找。
- 错误做法:
docker run -v ./my-certs:/app/certs my-app→ 容器内/app/certs不可见,因为嵌入FS已覆盖整个/app - 正确做法:打包前确保
dist/app/certs/localhost.crt和dist/app/certs/localhost.key存在,且Caddyfile路径与之匹配 - 若需多环境(dev/staging/prod),得为每套证书单独构建一个二进制,或改用非嵌入模式(即不设
EMBED),让证书由外部挂载
离线激活最难缠的点不在证书生成,而在FrankenPHP对“TLS上下文初始化”的强耦合——它把证书加载、ACME客户端、HTTP/3握手全绑在同一个启动阶段。一旦漏掉--http3-port=0或误用tls internal,进程就会在无提示状态下等待网络响应,连日志都不输出。建议首次离线启动时加--debug参数,观察日志里是否出现http: TLS handshake error或acme: getting directory这类关键词,它们是联网行为未被彻底切断的明确信号。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











