把HTTP/3回源当成普通TCP回源配置,日志里往往只剩下一行HTTP/3.0,看不到UDP四元组、QUIC连接ID这些排障信息。HTTP/3不再基于TCP,而是基于QUIC协议,底层传输使用UDP,因此日志字段的选择、连接标识的提取、排障时看的顺序都与传统HTTP/1.1或HTTP/2有区别。Nginx从1.25版本开始提供HTTP/3相关能力,日志模块也允许我们通过变量把协议、端口、TLS版本等信息记下来。

一、HTTP/3回源与UDP日志的底层关系
HTTP/3把传输层从TCP换成了UDP,这对Nginx日志的影响不在应用层请求格式,而在连接模型。TCP回源时,一条连接通常对应一个稳定的四元组,日志里的远端地址、远端端口、本地端口基本不变,排查超时和断连时也习惯按连接编号或TCP状态去查。UDP回源没有内核层面的连接状态,QUIC连接由连接ID标识,连接ID不与某个固定四元组绑定,客户端网络切换后可能更换IP或端口,但QUIC连接仍然可以继续使用。
这意味着在日志里只记录 HTTP/3.0 是不够的。你需要同时记录远端IP、远端端口、本地IP、本地端口,最好还能拿到QUIC连接ID和TLS版本。Nginx本身在客户端访问段能识别HTTP/3,并将协议版本写入 $server_protocol,对于TLS握手信息也会写入 $ssl_protocol。但要注意回源段是否真正走了HTTP/3:如果Nginx作为反向代理,上游服务器支持HTTP/3且Nginx启用了对应回源能力,才会产生UDP回源日志;如果Nginx到上游仍走HTTP/1.1或HTTP/2,日志中的HTTP/3只代表客户端到Nginx一段,不代表回源使用了UDP。
排查HTTP/3回源问题时,还要区分Nginx访问日志和错误日志。访问日志记录请求完成后的结果,而UDP传输中的丢包、重传、QUIC握手失败等信息更多出现在错误日志或QUIC调试日志中。只有把两者结合,才能判断一个请求失败是上游TCP回源的问题,还是HTTP/3 UDP回源的路径问题。
二、配置Nginx日志记录HTTP/3关键字段
要记录HTTP/3回源中的UDP信息,不能只使用默认的combined格式。默认格式没有 $server_protocol、$remote_port、$server_port 等字段。下面给出一个适用于HTTP/3排查的log_format配置,放在http块中:
log_format h3_access '$remote_addr:$remote_port -> $server_addr:$server_port '
'proto=$server_protocol quic=$http3 tls=$ssl_protocol '
'status=$status bytes=$body_bytes_sent '
'time=$request_time upstream=$upstream_response_time '
'request="$request"';
access_log /var/log/nginx/h3_access.log h3_access;
这个格式中,$remote_addr 和 $remote_port 记录客户端UDP四元组的一端,$server_addr 和 $server_port 记录Nginx监听端。$server_protocol 在HTTP/3下通常显示为 HTTP/3.0,$http3 是Nginx提供的HTTP/3特有变量,可用来判断该连接是否基于QUIC。$ssl_protocol 则记录TLS版本,因为HTTP/3的加密基于TLS 1.3,看到TLSv1.3与HTTP/3.0同时出现,基本可以确认是QUIC加密连接。
只看这些字段还不够,因为UDP回源时同一个QUIC连接可能在不同四元组之间迁移。若需要关联同一个QUIC连接的所有请求,建议在日志中增加连接ID。Nginx官方变量对QUIC连接ID的暴露程度较低,实际部署时可以借助access阶段的map或变量处理器,把可用的QUIC连接标识写入自定义变量。如果Nginx版本没有直接提供QUIC连接ID变量,可以查询当前版本的变量列表,或者通过debug日志中的quic connection id字段定位。接着可以用map变量区分HTTP/3请求,避免给所有访问日志增加不必要的字段:
map $http3 $is_h3 {
default 0;
h3 1;
}
map $http3 $is_plain {
default 1;
h3 0;
}
access_log /var/log/nginx/access.log combined if=$is_plain;
access_log /var/log/nginx/h3_access.log h3_access if=$is_h3;
上述配置让只有HTTP/3请求写入h3_access.log,普通HTTP请求继续写入combined。这样既能保留HTTP/3的详细字段,又不会让全量访问日志膨胀。需要注意的是,access_log的if参数在Nginx 1.7.0以后可用,但if条件会在请求处理的不同阶段生效,遇到复杂条件时建议先在测试环境验证日志写入情况。
三、UDP回源日志的排查思路与性能取舍
排查UDP回源问题时,日志顺序容易产生误导。UDP包在网络中可能乱序到达,QUIC虽然会在应用层排序和重传,但Nginx写日志的时间点由请求完成时间决定。你可能会看到后发起的请求先写日志,先发起的请求后写日志,这并不代表服务器处理顺序错乱。为了让时间线清晰,可以在日志格式里增加 $msec 和 $request_time。$msec 是请求开始时的时间戳,精度到毫秒,配合 $request_time 可以计算请求完成时间,比单纯依赖日志文件追加顺序可靠。
UDP回源还会影响性能统计。HTTP/3减少了握手开销,在弱网或高丢包场景下表现更好,但UDP日志如果记录过细,比如频繁打印每个QUIC连接ID、每次连接迁移的四元组变化,会增加磁盘写入压力。生产环境建议分两层记录:全量请求使用简单格式,只保留协议版本、状态码、响应时间;异常或慢请求再输出完整UDP字段。可以使用map和access_log的if条件把慢请求单独写到另一个文件,再根据阈值分析。
还有一点容易混淆:日志传输协议与回源协议是两回事。即使Nginx回源使用HTTP/3的UDP,访问日志本身可以由Nginx写入本地磁盘,或者通过syslog发送到日志平台。如果syslog配置为UDP传输,日志在网络中也可能丢包,此时排障会双重依赖UDP。对关键HTTP/3回源日志,优先写本地文件,再异步同步到集中日志系统,能避免回源问题与日志问题相互干扰。
四、实践建议
把HTTP/3回源日志配好之后,建议用一次完整的QUIC握手做验证。先关闭缓存,用支持HTTP/3的客户端访问一次Nginx,再检查h3_access.log中是否同时出现 proto=HTTP/3.0、quic=h3、tls=TLSv1.3 以及远端UDP端口。如果没有这些字段,说明客户端可能回退到TCP,或者Nginx的HTTP/3监听未生效。接着检查Nginx错误日志中是否有QUIC相关的握手错误,确认UDP 443端口在防火墙和安全组中放行。
对于上游回源,如果上游地址仍然是HTTPS但使用HTTP/1.1,那Nginx回源段不会出现UDP日志。要验证回源协议,可以在上游服务器上查看对应连接协议,或通过Nginx调试日志确认回源使用的传输协议。很多回源链路会同时支持HTTP/2和HTTP/3,只有实际协商为HTTP/3时,才需要关注UDP日志字段。
最终,HTTP/3日志的核心不是单纯记录协议名,而是把UDP无状态连接、QUIC连接ID、TLS版本和请求时间线组合起来。这样当出现回源超时、连接重置或高丢包时,日志不再是只有状态码的流水账,而能还原请求当时所处的传输上下文。