根本原因是 JWT 放在 Authorization 头中导致单个字段超出 http2_max_field_size 默认值(4KB),Nginx 在 HTTP/2 解帧阶段即拒绝请求并返回 431;需显式配置 http2_max_field_size(如 8k)并同步匹配 http2_max_header_size,且确保 CDN、LB 和客户端无更严限制。

直接结论:JWT 过长导致 431 Request Header Fields Too Large 错误,根本原因不是 http2_max_header_size 不够,而是单个字段(比如 Authorization)超出了 http2_max_field_size 默认值(4k),必须显式调大。
为什么 JWT 会触发 431 而不是 400 或 502
HTTP/2 协议层在解析请求头时,会先校验每个独立字段的长度。JWT 通常放在 Authorization: Bearer xxxxx... 中,整个 token 经 Base64 编码后很容易突破 4KB。此时 Nginx 在 HTTP/2 解帧阶段就拒绝该请求,返回标准的 431 状态码 —— 它比 400 更精确,表示“头部字段本身过大”,而非语法错误。你不会看到后端日志或 access_log 记录这次请求,因为压根没进到 location 匹配逻辑。
常见现象包括:
- 前端报错
net::ERR_HTTP2_PROTOCOL_ERROR或空响应 - curl -v 显示
,但无 body -
nginx error_log 出现
client sent too large header field
如何确认是 http2_max_field_size 触发的限制
不要凭感觉调参。先定位问题字段:
- 用
curl -v -H "Authorization: Bearer $(python3 -c "print('A' * 5000)")" https://your.site/api模拟长字段,复现 431 - 检查
error_log对应时间点的完整错误行,它会明确指出哪个字段名和长度超限,例如:client sent too large header field: "Authorization" while reading client request headers - 注意:
http2_max_header_size控制的是所有 header 总和,而这里报的是单个字段,说明瓶颈在http2_max_field_size
http2_max_field_size 的安全与兼容性权衡
调大这个值能解决问题,但不能无脑设成 64k。需结合实际场景判断:
- 典型 JWT(含 RSA-256 签名)Base64 编码后约 1.2–1.8KB;若使用 RSA-4096 或嵌入大量 claims(如用户权限树),可能达 5–8KB
- 设为
8k可覆盖 99% 的 JWT 场景,且仍留有余量防 DoS(比如恶意构造超长User-Agent) - 若设为
16k以上,需同步检查http2_max_header_size是否匹配(建议按 2–4 倍设置,如32k),否则可能被后者截断 - 该指令只能出现在
http或server块,不能写在location里
正确配置示例:
http {
http2_max_field_size 8k;
http2_max_header_size 32k;
http2_max_requests 5000;
<pre class="brush:php;toolbar:false;">server {
listen 443 ssl http2;
# ... 其他 SSL 和路由配置
}
}
容易被忽略的部署细节
改完配置不等于生效,这几个点常被跳过:
- 必须执行
nginx -t验证语法,Nginx 1.24+ 对 HTTP/2 参数校验更严格,拼写错误(如写成http2_max_filed_size)会导致 reload 失败 - reload 后用
ss -tlnp | grep :443确认监听套接字带http2标识,避免因旧进程残留导致配置未加载 - 浏览器访问时按 F12 → Network → 点开任意请求 → Headers → 查看
Protocol字段是否为h2,不是http/1.1 - 如果用了 CDN(如 Cloudflare),确保它透传原始 header 且自身也支持足够大的字段,否则问题会转移到 CDN 边缘节点
最麻烦的情况是:你在 Nginx 调大了参数,但上游 LB(如 ALB、SLB)或客户端 SDK(如 OkHttp 旧版本)仍有更严的 header 限制,这时 431 依然会出现,只是源头变了。











