背景
前端和测试反馈说登录功能出现 bug,通过 F12 定位到是 getUserInfo 接口返回 400
重点是这个接口的报错和登录的账号有关,有些账号报 400,有些账号报 403,还有些账号登录成功.
但是我通过 postman 请求,不管什么账户都能成功.而使用前端页面请求的确和前端测试反馈的一致.这点就非常奇怪.
定位问题
首先我发现页面 403 的请求明显没有传authorization字段,所以返回 403 是正常的,但是没有思考具体为什么登录接口有返回 token,但是前端没有带上.
400 的问题比较奇怪,没有任何 response,只有一个 status code 是 400.
因为异常和请求的账号有关,所以考虑到会不会是 token 不一样,解析出token 内部的数据导致的 bug,但是使用 postman 请求缺没有问题.
postman 请求和浏览器请求的不同点就在于浏览器请求还带上了很多的 header 参数,postman 由于是我后端自己测试,所以只添加了基本的authorization和Content-Type
查询资料发现,Nginx 服务器默认的 header 长度上限是 4k,如果超过这个值,Nginx 就会直接返回 400.通过想修改默认的 header 上限值,可以通过以下2个参数来调整nginx的header上限
- client_header_buffer_size 16k
- large_client_header_buffers 4 16k
原因
由于我在加密 token 的时候将太多数据添加到 payload 里了,导致 token 过长.
403 是由于 token 过长,超过了浏览器支持的最大上限,所以根本添加不上去.
400 也是因为 token 过长,只不过没有超过浏览器的上限,但是加上其他的 header 参数,总长度超过了 Nginx 服务的默认长度上限.
解决方法
- 缩小 header,将多余的数据从 payload 中剔除,token 就会变短.
- 修改 Nginx header 的上限
修改 Nginx header 的上限的参数
nginx处理header时的方法:
- 先处理请求的request_line
- 之后才是request_header
这两者的buffer分配策略相同。
先根据client_header_buffer_size配置的值分配一个buffer,如果分配的buffer无法容纳 request_line/request_header,那么就会再次根据large_client_header_buffers配置的参数分配large_buffer,如果large_buffer还是无法容纳,那么就会返回414(处理request_line)/400(处理request_header)错误。
如果你的请求中的header都很大,那么应该使用client_header_buffer_size,这样能减少一次内存分配。
如果你的请求中只有少量请求header很大,那么应该使用large_client_header_buffers,因为这样就仅需在处理大header时才会分配更多的空间,从而减少无谓的内存空间浪费。
针对get请求,解决请求串过长的问题:
针对get请求,我们可以通过修改另外两个配置来解决请求串超长的问题:client_header_buffer_size语法:client_header_buffer_size size默认值:1k使用字段:http, server这个指令指定客户端请求的http头部缓冲区大小绝大多数情况下一个头部请求的大小不会大于1k不过如果有来自于wap客户端的较大的cookie它可能会大于1k,Nginx将分配给它一个更大的缓冲区,这个值可以在large_client_header_buffers里面设置。large_client_header_buffers语法:large_client_header_buffers number size默认值:large_client_header_buffers 4 4k/8k使用字段:http, server指令指定客户端请求的一些比较大的头文件到缓冲区的最大值,如果一个请求的URI大小超过这个值,服务器将返回一个”Request URI too large” (414),同样,如果一个请求的头部字段大于这个值,服务器将返回”Bad request” (400)。缓冲区根据需求的不同是分开的。默认一个缓冲区大小为操作系统中分页文件大小,通常是4k或8k,如果一个连接请求将状态转换为keep-alive,这个缓冲区将被释放。
那么有人就会觉得奇怪了,为什么修改http header的大小就能解决get请求串过长的问题呢,这就要从http协议的get请求说起了,其实GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中)。