http/3硬性依赖udp 443端口映射,未配置则quic连接失败、降级http/2;docker需显式-p 443:443/udp,防火墙与云安全组也须放行udp 443。

HTTP/3 依赖 UDP 端口映射,不加就用不了
FrankenPHP 启用 HTTP/3(即 QUIC)时,443:443/udp 这条映射不是可选的“锦上添花”,而是协议层硬性要求。Caddy 内部用 UDP socket 监听 QUIC 流量,如果容器或宿主机没把 UDP 443 暴露出去,客户端发起 HTTP/3 请求会直接超时或降级到 HTTP/2。
常见错误现象包括:
-
curl --http3 https://localhost报HTTP/3 not supported或卡住无响应 - 浏览器 DevTools 的 Network 面板里看不到
h3协议标识,alt-svc响应头缺失或无效 - Caddy 日志出现
failed to listen on UDP或quic: failed to bind
实操建议:
- Docker 运行时必须显式加
-p 443:443/udp,不能只写-p 443:443(那是 TCP) - 如果你用
compose.yaml,ports 下要单独列一条"443:443/udp",和 TCP 的"443:443"并列 - 宿主机防火墙(如
ufw或firewalld)需放行 UDP 443,否则即使容器映射了也收不到包 - 云服务器(如 AWS EC2、阿里云 ECS)安全组规则里,UDP 443 也要手动打开
只跑 HTTP/1.1 或 HTTP/2 的话,UDP 映射可以去掉
如果你明确不需要 HTTP/3,比如目标客户端全是老旧浏览器、内部服务调用走 HTTP/2 就够用,或者压测发现 QUIC 在当前网络环境下反而不稳定,那完全可以不映射 UDP 端口。
这种情况下:
- Caddy 会自动禁用 HTTP/3,
alt-svc头不会出现,curl --http3会失败但不影响正常 HTTPS 访问 - 启动日志里不会有 QUIC 相关报错,资源占用略低(少一个 UDP socket)
- 配置更“保守”,适合某些受限网络环境(如部分企业内网 NAT 设备对 UDP 443 支持差)
但注意:frankenphp 默认 Caddyfile 里只要启用了 experimental_http3,它就会尝试监听 UDP —— 所以要么配好 UDP 映射,要么在 Caddyfile 全局块里删掉或注释掉该指令。
Docker 和独立二进制对 UDP 的处理差异
用 docker run 时,UDP 映射是显式声明的,漏了就是漏了;而用独立二进制(如 frankenphp php-server)直接运行,它默认会尝试绑定 0.0.0.0:443 的 TCP 和 UDP,但能否成功取决于宿主机权限和端口占用情况。
关键区别点:
- Docker 容器内进程默认没有
CAP_NET_BIND_SERVICE,所以它无法直接 bind 443,必须靠-p映射;而独立二进制若以 root 运行,能自己 bind,但 UDP 绑定失败时往往静默忽略,不报错也不提示 - 独立二进制下想确认 UDP 是否生效,得看启动日志有没有
listening on :443 (udp)这类输出,而不是只看 TCP 行 - macOS 上用独立二进制跑,可能因 SIP 限制无法 bind UDP 443,此时 Docker 反而更可靠
UDP 端口映射这件事,表面是加一行命令,背后牵扯协议支持、网络策略、运行时权限三层判断。很多人调了半天 HTTP/3 不生效,最后发现只是 Docker 命令里少了个 /udp 后缀 —— 这个细节太容易被忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











