Nginx 的 client_header_buffer_size 和 large_client_header_buffers 学习

阅读数:89 2019 年 11 月 19 日 23:40

Nginx的client_header_buffer_size和large_client_header_buffers学习

之前看到有人写的一篇关于 nginx 配置中 large_client_header_buffers 的问题排查的文章,其中提到:large_client_header_buffers 虽然也可以在 server{}内生效,但是只有 低于 nginx 主配置中的值才有意义。

对这个结论,我心存疑虑,总觉得这种设计很奇怪,于是自己做了个测试,希望能了解的更深入一些。

测试方法

nginx 主配置中加入配置项:(在主配置中将 header 大小控制在 1k)

Nginx的client_header_buffer_size和large_client_header_buffers学习

删除所有干扰 vhost,仅留下一个:

Nginx的client_header_buffer_size和large_client_header_buffers学习

构造请求的 shell:(构造 header 超过 1k 的请求)

Nginx的client_header_buffer_size和large_client_header_buffers学习

1 第一次测试结果

测试得到的结果和之前看到的文章的结果不同,该长 url 请求成功被 nginx 处理。

什么情况啊?于是查看和文章中环境上的不同,发现很重要的一点:我只有这一个 vhost。

于是添加了另外一个 vhost,添加 vhost 配置如下:(没有设置 large_client_header_buffers)

Nginx的client_header_buffer_size和large_client_header_buffers学习

2 第二次测试结果

测试发现,nginx 依旧可以处理该长 url 请求。

再次思考不同点,想到:这些 vhost 是被主配置中 include 进来的,是否会和读取顺序有关呢?

于是再次调整配置,将两个 vhost 放到了一个 conf 文件中,配置如下:

Nginx的client_header_buffer_size和large_client_header_buffers学习

3 第三次测试结果

得到和文章中相同的结果,nginx 返回 414 Request-URI Too Large。

带着好奇心,我颠倒了下两个 vhost 的顺序,如下:

Nginx的client_header_buffer_size和large_client_header_buffers学习

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 指令入手,先查看这个指令的定义部分:

Nginx的client_header_buffer_size和large_client_header_buffers学习

由定义看到,我们在 server{}中解析到的值会和 http{}中的值做一次 merge,作为该 server{}下的最终值。查看 merge 相关的逻辑:

Nginx的client_header_buffer_size和large_client_header_buffers学习Nginx的client_header_buffer_size和large_client_header_buffers学习

从这段逻辑中得到结论:如果我们在 server{}中配置了 client_header_buffer_size,那么针对这个 server{}块的最终值应该就是我们配置的值。

为了印证我的结论,我重新写了 vhost 配置,并在代码中加入调试信息,把最终结果打印出来:

Nginx的client_header_buffer_size和large_client_header_buffers学习

调试代码:

Nginx的client_header_buffer_size和large_client_header_buffers学习

重新编译 nginx,测试每个 server{}中 client_header_buffer_size 的最终值为:

Nginx的client_header_buffer_size和large_client_header_buffers学习

从值的最终结果看,的确是之前设置的 1m,但是请求时却返回了 414。

由于将两个 server{}的位置颠倒后可以正常处理请求,所以在颠倒的情况下又测试了下最终值,输出如下:

Nginx的client_header_buffer_size和large_client_header_buffers学习

最终值的输出还是 1m,但是这次就可以正常处理请求了。

看来 nginx 在实际处理请求的过程中,一定还有之前不知道的一套逻辑,用来判断

client_header_buffer_size 的最终值。

nginx 处理请求时的相关代码如下:

Nginx的client_header_buffer_size和large_client_header_buffers学习

这里真相大白:

原来 client_header_buffer_size 的最终值,是 nginx 在解析 conf 后,default_server 中经过 merge 的最终值。

而 default_server 在 nginx 中的定义为:在 listen 指令中定义:

Nginx的client_header_buffer_size和large_client_header_buffers学习

为了验证这一点,我修改 vhost 配置为:

Nginx的client_header_buffer_size和large_client_header_buffers学习

重启 nginx 观察 merge 结果:

Nginx的client_header_buffer_size和large_client_header_buffers学习

merge 结果没有不同。测试请求,这次 nginx 成功处理该请求,和预期的效果一致。

结束语

笔者又测试了 large_client_header_buffers,得到和 client_header_buffer_size 同样的结果。可以得出结论:nginx 在处理 header 时实际分配的 buffer 大小,是解析 conf 后,default_server 中的最终值。

个人水平有限,上面的测试方法和理解如有不当的地方,还望大家指正,谢谢!

本文转载自公众号 360 云计算(ID:hulktalk)。

原文链接:

https://mp.weixin.qq.com/s/cA6vxQYQEIsdAtEgXgGCYw

评论

发布