女主宣言
之前看到有人写的一篇关于nginx配置中large_client_header_buffers的问题排查的文章,其中提到:large_client_header_buffers 虽然也可以在server{}内生效,但是只有 低于 nginx主配置中的值才有意义。
对这个结论,我心存疑虑,总觉得这种设计很奇怪,于是自己做了个测试,希望能了解的更深入一些。
PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!
测试方法
nginx主配置中加入配置项:(在主配置中将header大小控制在1k)
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/a13e5df7341a62bdabbaa2f099ce5220.jpeg)
删除所有干扰vhost,仅留下一个:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/aa66d4cb54900851afa714340e048694.jpeg)
构造请求的shell:(构造header超过1k的请求)
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/88c5578e545075530024408e8436d431.jpeg)
1
第一次测试结果
测试得到的结果和之前看到的文章的结果不同,该长url请求成功被nginx处理。
什么情况啊?于是查看和文章中环境上的不同,发现很重要的一点:我只有这一个vhost。
于是添加了另外一个vhost,添加vhost配置如下:(没有设置 large_client_header_buffers)
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/e347279c4b9fcfb4401c1d1aa290e217.jpeg)
2
第二次测试结果
测试发现,nginx依旧可以处理该长url请求。
再次思考不同点,想到:这些vhost是被主配置中include进来的,是否会和读取顺序有关呢?
于是再次调整配置,将两个vhost放到了一个conf文件中,配置如下:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/b080b0a4b74b3d916badfe9b4d1a58a4.jpeg)
3
第三次测试结果
得到和文章中相同的结果,nginx返回414 Request-URI Too Large。
带着好奇心,我颠倒了下两个vhost的顺序,如下:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/c4760b123815229ec7c8126b8c29da35.jpeg)
4
第四次测试结果
nginx成功处理该长url请求。
初步结论
通过上面的现象,我得到一个初步结论:在第一个vhost中配置的large_client_header_buffers参数会起作用。
好奇怪的现象啊,我对自己得出的结论也是心存疑惑,于是决定从手册中好好读下控制header_buffer相关的指令。
从手册上理解nginx有关header_buffer配置指令
从手册上找到有两个指令和header_buffer有关:
1.client_header_buffer_size
2.large_client_header_buffers
对nginx处理header时的方法,学习后理解如下:
1.先处理请求的request_line,之后才是request_header。
2.这两者的buffer分配策略相同。
3.先根据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_buffer时的使用场景是不同的,个人理解如下:
1.如果你的请求中的header都很大,那么应该使用client_header_buffer_size,这样能减少一次内存分配。
2.如果你的请求中只有少量请求header很大,那么应该使用large_client_header_buffers,因为这样就仅需在处理大header时才会分配更多的空间,从而减少无谓的内存空间浪费。
为了印证自己对两个配置指令的理解,我把large_client_header_buffer
换成client_header_buffer_size,重新跑上面的多种测试,得到了和之前各种场景相同的结论。
手册上也只是说明了这两个指令的使用场景,没有说更多的东西了,之前的疑惑还是没有得到解答,那么只有最后一招了,也是绝招:从源码中寻找答案!
源码学习
这里从client_header_buffer_size指令入手,先查看这个指令的定义部分:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/0ba307d1bcb2bfce45ed64f7dd498659.jpeg)
由定义看到,我们在server{}中解析到的值会和http{}中的值做一次merge,作为该server{}下的最终值。查看merge相关的逻辑:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/2daa1382c1e5a1c32a490627cf149b39.jpeg)
从这段逻辑中得到结论:如果我们在server{}中配置了client_header_buffer_size,那么针对这个server{}块的最终值应该就是我们配置的值。
为了印证我的结论,我重新写了vhost配置,并在代码中加入调试信息,把最终结果打印出来:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/795a4f85c550b258ba4bfc2de6cce127.jpeg)
调试代码:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/99820115a7d9fa6551793d724623b4c0.jpeg)
重新编译nginx,测试每个server{}中client_header_buffer_size的最终值为:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/d54a5d43acb4bb297f7d4215baa565cb.jpeg)
从值的最终结果看,的确是之前设置的1m,但是请求时却返回了414。
由于将两个server{}的位置颠倒后可以正常处理请求,所以在颠倒的情况下又测试了下最终值,输出如下:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/dd0d78a2aa09d0ca8f68a4a03588c939.jpeg)
最终值的输出还是1m,但是这次就可以正常处理请求了。
看来nginx在实际处理请求的过程中,一定还有之前不知道的一套逻辑,用来判断
client_header_buffer_size的最终值。
nginx处理请求时的相关代码如下:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/3c06cd6d5415ed05fd0232d998594fc6.jpeg)
这里真相大白:
原来client_header_buffer_size的最终值,是nginx在解析conf后,default_server中经过merge的最终值。
而default_server在nginx中的定义为:在listen指令中定义:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/783261c14489e9c6e831c4b6c13d3daf.jpeg)
为了验证这一点,我修改vhost配置为:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/e55ee10cd7f11d40b3d1c5edcedd77d8.jpeg)
重启nginx观察merge结果:
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/26feeb1fb36c82d1479270c415c6718b.jpeg)
merge结果没有不同。测试请求,这次nginx成功处理该请求,和预期的效果一致。
结束语
笔者又测试了large_client_header_buffers,得到和client_header_buffer_size同样的结果。可以得出结论:nginx在处理header时实际分配的buffer大小,是解析conf后,default_server中的最终值。
个人水平有限,上面的测试方法和理解如有不当的地方,还望大家指正,谢谢!
360云计算
由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/a172e227eb595e470acf718aefdfada5.jpeg)