FrankenPHP本身不实现HTTP/3,依赖内置Caddy处理TLS+QUIC;Kubernetes中需Service透传443端口、Caddyfile显式启用experimental_http3、使用真实域名触发ACME证书申请,并通过alt-svc响应头验证。

FrankenPHP 本身不监听 HTTP/3 端口,它依赖 Caddy 的 TLS+QUIC 层
HTTP/3 不是 FrankenPHP 自己实现的协议,而是由内置 Caddy 负责处理。FrankenPHP 只暴露一个 HTTP/1.1 或 HTTPS 的后端接口(通常是 :80 或 :443),Caddy 在前端完成 TLS 终止、ALPN 协商和 QUIC 封装。所以你在 Kubernetes 中「暴露 HTTP/3」,本质是让 Caddy 能正常监听 :443 并启用 experimental_http3,同时确保流量能抵达容器的 443 端口。
Kubernetes Service 必须暴露 443 并透传 TLS 流量
若用 LoadBalancer 或 NodePort 类型 Service 直接暴露 FrankenPHP 容器,必须满足:
- Service 的
ports[].port和ports[].targetPort都设为443(不能只暴露 80) - Service 的
protocol必须是TCP(HTTP/3 基于 QUIC,但底层仍走 UDP;Kubernetes 原生 Service 不支持 UDP 暴露 HTTP/3,所以实际靠 TCP 443 + ALPN 协商降级到 QUIC) - 不要在 Service 层做 TLS 终止——证书必须由容器内 Caddy 管理,否则 ALPN 扩展无法传递,
alt-svc头不会出现 - 云厂商 LoadBalancer(如阿里云 SLB)需开启「透传模式」或「TLS 直通」,禁用其自带的 HTTPS 卸载功能
Caddyfile 必须显式启用 experimental_http3
仅靠默认配置,Caddy 不会自动开 HTTP/3。你得挂载自定义 Caddyfile,并在全局块中声明:
{<br> servers {<br> protocol {<br> experimental_http3<br> }<br> }<br> frankenphp {<br> num_threads auto<br> }<br>}<br>yourdomain.com {<br> php_server<br>}
注意:yourdomain.com 必须是真实可解析域名,且 Caddy 能通过 80/443 端口完成 ACME 验证(所以安全组要放行这两个端口);localhost 或 127.0.0.1 域名无法触发证书申请,也就没有 HTTPS/HTTP/3。
立即学习“PHP免费学习笔记(深入)”;
验证 HTTP/3 是否生效的关键点
别只看 curl 是否返回成功——HTTP/3 成功的唯一可靠信号是响应头含 alt-svc:
- 用真实域名访问:
curl -I https://yourdomain.com --http3 - 响应中必须有类似
alt-svc: h3=":443"; ma=86400的字段 - 如果返回
502 Bad Gateway或curl: (XX) http3 error,大概率是 Service 没透传 443、Caddyfile 缺experimental_http3、或域名未备案/未解析导致 ACME 失败 - Kubernetes Pod 日志里应出现
http: TLS handshake error或http: server listening on :443,没这些说明 Caddy 根本没起来
最容易被忽略的是:HTTP/3 不是“开了端口就通”,它强依赖域名、证书、ALPN 和客户端支持——哪怕 Service 和 Caddyfile 全对,只要 DNS 解析慢半秒,ACME 就可能超时,整个链路就断在第一步。



















