http/3流量实际走udp 443端口而非tcp,防火墙、cdn及docker等必须显式放行udp 443,否则请求静默丢弃;frankenphp需cap_net_bind_service权限或root运行才能绑定udp socket。

HTTP/3 流量根本不在 TCP 443 上跑
FrankenPHP 开启 HTTP/3 后,浏览器发起的 HTTP/3 请求实际走的是 UDP 443,不是 TCP。哪怕你配置了 https://example.com,底层协议协商(ALPN)成功后,客户端会主动切换到 UDP 套接字发包。TCP 443 只负责初始 TLS 握手和 HTTP/1.1 或 HTTP/2 回退;真正承载 HTTP/3 数据的是独立的 UDP 连接。不放行 UDP 443,等于直接掐断 HTTP/3 的物理通路。
防火墙/安全组默认拦掉所有 UDP 流量
绝大多数云平台(阿里云、腾讯云、AWS)和本地防火墙(ufw、iptables)默认策略是:只放行显式声明的 UDP 端口。TCP 443 通常已开放,但 UDP 443 是完全独立的规则项,不会自动继承。现象很典型:Chrome DevTools Network 面板里能看到 h3 协议标识短暂出现又消失,curl --http3 直接超时,tcpdump -i any udp port 443 抓不到任何入向包——这就是被中间设备静默丢弃了。
FrankenPHP 的 UDP 绑定依赖系统权限与网络可达性
FrankenPHP 启动时调用 http3.Server,它内部会尝试绑定 :443 的 UDP socket。这个动作需要两个前提:
- 进程有
CAP_NET_BIND_SERVICE权限(Docker 中需加--cap-add=NET_BIND_SERVICE),或以 root 运行(生产环境不推荐) - 操作系统能收到目的端口为 443 的 UDP 包,即路径上无防火墙/安全组拦截
缺任一条件,FrankenPHP 日志里不会报错,但 ss -ulpn | grep ':443' 查不到 UDP 监听,curl -v --http3 https://localhost 会 fallback 到 HTTP/2。
宝塔面板或 Docker Compose 必须显式声明 UDP 端口映射
在宝塔中,仅在「网站」设置里勾选「HTTP/3 支持」远远不够;必须进【安全】→【放行端口】→填 443 + 选协议 UDP。Docker 用户更易踩坑:ports: ["443:443"] 默认只映射 TCP,必须写成:
ports: - "80:80" - "443:443" - "443:443/udp"
少这一行,容器内 FrankenPHP 虽然绑定了 UDP,宿主机根本收不到外部 UDP 包——这是最常被忽略的配置点。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











