前言
最近业务碰到了一个诡异的400接口请求异常,部门用户通过浏览器访问会出现400响应码错误,部分用户又能正常访问。该接口用postman请求访问,都能正常返回数据。后端写客户端请求该接口,也都能返回正常的数据。本文就来记录一下这次问题
整体简化版请求链路
如图
问题排查过程
因为不管是用postman或者是后端自己写客户端请求,都能返回正常的数据。就说明我们请求的参数是没啥问题,问题可能是出现在请求头上,我们就觉得是不是nginx做了啥限制,于是 官网溜一圈,发现如图的参数
它的中文大意是通常客户端请求缓存的大小1K就够了,但是如果请求包含长cookie或者wap客户端,1k这个值可能就不大够,因此我们适当把该大小调高验证,发现没效果。于是我们把目光转向,client_header_buffer_size下方的large_client_header_buffers
该参数的大体作用:设置用于读取大型客户端请求标头的缓冲区的最大数量和大小。请求行如果超过一个缓冲区的大小,就会向客户端返回414(请求URI太大)错误。请求头字段也不能超过一个缓冲区的大小,否则会向客户端返回400(错误请求)错误。缓冲区仅按需分配。默认情况下,缓冲区大小等于8K字节。如果在请求处理结束后,连接转换为保持活动状态,则释放这些缓冲区。
看到这里我们似乎看到曙光,因此我们果断把该参数加上,并调高相应的配置值,本以为可以高枕无忧,结果配上去,那偌大的400错误,感觉就是在嘲讽我们的天真。
思路似乎断了,就问了一下chatGPT,看它有没有什么想法,可能是我提示不够精确,在它一本正经的胡说八道后,我放弃继续追问。于是还是走回传统的提问方式,去搜索引擎排查一番,然后查到这篇文章
https://www.likecs.com/ask-3802541.html
这篇文章告诉我们当我们做了刚才配置后,还会出现400,此时跟我们的nginx大概没啥关系了,应该是跟后端有关系,他建议是如果是springboot项目,通过在项目中适当调高如下的值
server:
max-http-header-size:
于是我们死马当活马医,神奇的事发生了,没有再出现400的情况
问题原因梳理
出现请求400的原因,确实是请求头过大的原因,但为什么通过postman或者后端请求就不会有问题,而通过浏览器访问就会有问题,原因就是我们在处理跨域的时候,请求头加了一堆乱七八糟的东西,比如
add_header 'Access-Control-Allow-Headers' 'x-app-id,x-tenant-id,x-user-id,Authorization,Accept,Origin,Keep-Alive,User-Agent,access-control-allow-origin,If-Modified-Since,Cache-Control,Content-Type,Range,systemId,Cookie。。。';
其次为什么会出现部分用户访问出现400,部分用户又不会,就是因为我们请求时,请求头会携带用户jwt token,该token的payload存放一堆跟用户相关的东西,比如权限id列表啥的,这样就导致有些用户的token的长度是比较大
总结
此次400响应码错误的问题,除了技术层面上,还有一些是规范上的,比如请求头加了了一堆无用的参数,其次为了方便,在token上搞了一堆业务数据,有些bug真的是无意识产生的,轻描淡写的一篇文章,可知道当时排查了2,3天,希望这篇文章能给其他小伙伴带来一些帮助或者排查思路吧
附录
nginx-ingress配置header头缓冲大小
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/server-snippet: |-
underscores_in_headers on;
large_client_header_buffers 4 1M;
client_header_buffer_size 16K;