FrankenPHP本地HTTPS必须用mkcert生成并信任自签名证书,因Let’s Encrypt等CA拒绝为localhost签发证书,Caddy自动HTTPS会失败;需运行mkcert -install信任根,再执行mkcert localhost 127.0.0.1 ::1生成证书,最后在Caddyfile中显式配置tls路径并监听如:4433端口。

FrankenPHP 本地跑 HTTPS 不靠 Let’s Encrypt(它不给 localhost 发证),必须用自签名证书 + mkcert 信任根,否则浏览器直接拦截、fetch() 拒绝、Service Worker 无法注册。
为什么不能直接用 Caddy 的自动 HTTPS?
Caddy 的 ACME 客户端只对公网可访问域名(如 example.com)有效;localhost 和 127.0.0.1 被所有 CA 明确拒绝签发证书。尝试配置 localhost { tls } 会卡在 ACME 挑战阶段,日志报错类似:HTTP 400: urn:ietf:params:acme:error:rejectedIdentifier。
所以本地 HTTPS 唯一靠谱路径是:自己生成可信的自签名证书。
用 mkcert 生成并信任 localhost 证书
这是目前最稳定、跨平台、浏览器零警告的方式。OpenSSL 手动签容易漏 subjectAltName,Chrome 会直接报 NET::ERR_CERT_INVALID。
立即学习“PHP免费学习笔记(深入)”;
- 安装
mkcert:brew install mkcert(macOS)、choco install mkcert(Windows)、或从 GitHub release 下载二进制 - 运行一次信任根:
mkcert -install(这步必须做,否则浏览器不认后续证书) - 为本地生成证书:
mkcert localhost 127.0.0.1 ::1 - 你会得到两个文件:
localhost+2.pem(证书)和localhost+2-key.pem(私钥)
把这两个文件放到项目目录下,比如 ./certs/,后面 Caddyfile 里会引用它们。
Caddyfile 中启用 HTTPS 监听(非自动 ACME)
FrankenPHP 默认只监听 HTTP(:80 或 :443 但无 TLS)。要启用 HTTPS,必须显式配置 TLS 证书路径,并指定监听端口(通常用 :4433 避开系统端口权限问题)。
你的 Caddyfile 应该长这样:
{
frankenphp {
num_threads auto
}
}
localhost:4433 {
tls ./certs/localhost+2.pem ./certs/localhost+2-key.pem
php_server
encode zstd br gzip
}
注意点:
-
tls指令后必须跟两个参数:证书文件路径 + 私钥文件路径,顺序不能反 - 不要写
tls on或tls { }空块,那会触发 ACME 流程 - 端口选
:4433而不是:443,避免 sudo 权限;浏览器访问时输https://localhost:4433 -
php_server必须在站点块内,且不能和reverse_proxy混用(它不是 FastCGI 代理)
启动后验证 HTTPS 是否生效
运行 frankenphp run(当前目录有 Caddyfile),然后:
- 浏览器打开
https://localhost:4433—— 地址栏应显示“安全”锁图标(首次可能需点“高级 → 继续前往”) - 终端执行:
curl -I https://localhost:4433 --insecure,响应头应含HTTP/2 200或HTTP/3 200(如果启用了experimental_http3) - 检查 PHP 是否正常工作:
echo "<?php echo 'HTTPS OK'; ?>" > index.php,再刷新页面
最容易被忽略的是:证书路径写错、mkcert -install 没运行、或者 Caddyfile 放错位置(FrankenPHP 默认只读当前工作目录下的 Caddyfile,不是 /etc/frankenphp/Caddyfile)。



















