标题:【Nginx09】Nginx学习:HTTP核心模块(六)请求头处理

文章目录
    分类:存储运维 标签:Nginx

    Nginx学习:HTTP核心模块(六)请求头处理

    对于一个 HTTP 应用来说,最重要的其实就是 HTTP 的两个核心功能,一个是请求,一个就是响应。而对于一个 Web 应用服务器来说,响应通常是静态文件或者是动态程序代码来完成,围绕响应的配置指令大部分以缓存优化为主。从这里也能看出,在 Nginx 这种应用服务中,请求相关的内容会更多一些,因为我们要面对的,要对接的,就是从外部不断发过来的请求。


    今天,我们先了解一下请求头相关的配置指令。

    请求头

    通用的 HTTP 请求头相关的配置主要也是大小、超时时间等等。它们都可以配置在 http、server 下面,我们一个一个来看下。

    client_header_buffer_size

    用于设置读取客户端请求头部的缓冲容量。

    client_header_buffer_size size;

    默认值是 1k ,对于大多数请求,1K的缓冲足矣。但如果请求中含有的cookie很长,或者请求来自WAP的客户端,可能请求头不能放在1K的缓冲中。 如果从请求行,或者某个请求头开始不能完整的放在这块空间中,那么 Nginx 将按照 large_client_header_buffers 指令的配置分配更多更大的缓冲来存放。


    注意哦,这里和请求体不同的是,请求体会往文件里放,但请求头不会,不够了再根据其它配置申请更大的内存。毕竟请求头的内容再大也大不到像需要上传文件的请求体一样。最终它的配置其实不会导致什么影响,因为最终如果不够了它会根据 large_client_header_buffers 的配置进行申请分配,因此,我们紧接着就看看 large_client_header_buffers 的配置。

    large_client_header_buffers

    这个配置是设置读取客户端请求超大请求的缓冲最大 number(数量) 和每块缓冲的 size(容量) 。

    large_client_header_buffers number size;

    它的默认值是 4 8k 。条件包括这么几点:

    • HTTP 请求行的长度不能超过一块缓冲的容量,否则nginx返回错误414 (Request-URI Too Large)到客户端。

    • 每个请求头的长度也不能超过一块缓冲的容量,否则nginx返回错误400 (Bad Request)到客户端。

    • (请求行+请求头) 的大小不能超过 32k(4 * 8k) 。

    即使 Nginx 处理完请求后与客户端保持长连接,Nginx 也会释放这些缓冲。如果在服务器级别指定该指令,则可以使用默认服务器的值。好了,咱们来测试一下。首先配置一下 Nginx 。

    // nginx.conf
    ……
    server {
    	client_header_buffer_size 256;
    	large_client_header_buffers 1 512;
    	……
    }
    ……

    注意,large_client_header_buffers 第二个参数必须要大于等于 connection_pool_size 这个配置项的大小,我这里默认是 512 ,所以这里只能配置为 512 ;第一个参数也不能设置为 0 ,必须是大于 0 的数字。


    接下来进行测试,现在这个情况,其实只要头部有一个大字符的参数,或者请求行(就是 URL 行)比较长,就会出 400 的错误。

    GET http://192.168.56.88/?bigparams=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnop
    
    User-Agent: PostmanRuntime/7.29.2
    Accept: */*
    Postman-Token: 328e2a7a-84e9-4622-9c83-2bdeb67ea94a
    Host: 192.168.56.88
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive

    上面的请求数据试一下,正好 513 个字符,直接报 400 Request Header Or Cookie Too Large 错误。或者直接一个大的请求头。下面这样的请求也会报错。

    GET http://192.168.56.88/
    
    LongHeader: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc
    User-Agent: PostmanRuntime/7.29.2
    Accept: */*
    Postman-Token: 200ad116-daca-4129-81a3-c17039021865
    Host: 192.168.56.88
    Accept-Encoding: gzip, deflate, br
    Connection: keep-alive

    上面的两个请求其实对应的是第三个条件,也就是请求行和请求头的和超过了 number * size 的大小,现在我们设置的就是 1 * 512 ,因此现在最大的大小就是 512 字节。接下来我们修改一下,将 number 改为 2 ,上面的请求都可以正常访问了,接下来我们测另外两种情况。

    large_client_header_buffers 2 512;

    先测试第二个条件,请求头中的一个请求项超过一块缓冲的容量,也就是有一个请求头项的大小超过 512 字节就可以了。

    GET http://192.168.56.88/
    ....
    LongHeader: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde
    ....

    正好 512 个字节,直接报 400 Request Header Or Cookie Too Large 错误。


    最后,我们再测试请求行,如果超长了,会不会返回 414 错误。

    GET http://192.168.56.88/?bigparams=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr

    使用这个请求,正好卡在报错的长度节点上,会返回 414 Request-URI Too Large 错误。


    现在你应该了解了吧,如果我们在日常的应用中出现了 400 或者 414 的报错信息,可以来检查一下这两个的配置是否有问题。

    client_header_timeout

    定义读取客户端请求头部的超时时间。

    client_header_timeout time;

    默认值是 60s 如果客户端在这段时间内没有传送完整的头部到 Nginx ,Nginx 将返回错误 408 (Request Time-out) 到客户端。和 client_body_timeout 一样,不知道咋测,但这里给出了一个 408 的错误码,如果发现了这个码的错误信息,可以过来尝试调大 client_header_timeout 和 client_body_timeout 数值,或者查查接收不到头部信息的原因。

    max_ranges

    如果请求中含有字节范围的请求头,这条指令可以限制此范围允许的最大值。

    max_ranges number;

    如果请求头的值超过此限制,将按请求未携带此请求头的情况处理。 默认nginx对此不做限制。设置为 0 将使 Nginx 完全不支持 HTTP 字节范围特性。


    啥意思呢?其实我也没看明白,那么咱们就来做实验。先构造请求头,也就是加上 Range 请求头。

    GET http://192.168.56.88/
    
    …………
    Range: bytes=0-100,100-200,200-300
    …………

    直接请求的话,服务器会返回 206 Partial Content 状态码。这个是 HTTP 的一种分段获取部分资源的状态码。具体的内容不清楚的同学可以自己查阅一下相关的资料哦。


    目前是默认的情况,接下来我们配置一下 max_ranges ,先指定为一个 0 。

    // nginx.conf
    ……
    server {
    	max_ranges 0;
    	……
    }
    ……

    重载配置后我们在客户端重新请求,会发现返回的状态码变成了 200 ,也就是说设置为 0 将使 Nginx 完全不支持 HTTP 字节范围特性这一点被我们证实了。接下来,再将它设置成 2 。

    max_ranges 2;

    再次请求后会发现返回的状态码还是 200 ,那么我们再将它调到 3 试一下,可以看到,现在又正常返回 206 了。现在你知道这个配置项的作用了吧。影响的就是 Range 请求头中范围项的数量,默认不限制就是只要有这个头就返回 206 ,如果设置为 0 ,就不管有没有都返回 200 ,如果指定为具体的数字,就是根据 Range 中的范围项数量(0-100表示一项)。

    underscores_in_headers

    允许或禁止在客户端请求头中使用下划线。

    underscores_in_headers on | off;

    默认是 off ,如果禁止,含有下划线的请求头将被标志为非法请求头并接受 ignore_invalid_headers 指令的处理。可以在默认主机的 server 配置级别定义此命令。这样,指令设置将覆盖监听同一地址和端口的所有虚拟主机。我们结合下面的配置指令一起看。

    ignore_invalid_headers

    控制是否忽略非法的请求头字段名。

    ignore_invalid_headers on | off;

    默认 on ,合法的名字是由英文字母、数字和连字符组成,当然也可以包含下划线 (由underscores_in_headers指令控制)。本指令可以在默认虚拟主机的 server 配置层级中定义一次,那么这个值在监听在相同地址和端口的所有虚拟主机上都生效。


    好了,来测试一下,我们需要先把 PHP 环境配上,或者你是使用其它语言也可以配上其它语言的 Nginx 环境。然后打印一下请求头中的信息。

    // nginx.conf
    ……
    server {
    	location ~ \.php$ {
    		root html;
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    	}
    	……
    }
    ……
    
    // html/1.php
    <?php 
    print_r($_SEREVER);

    然后,在 Postman 中构造一个请求头,并请求刚刚创建的 php 文件。

    GET http://192.168.56.88/1.php
    
    Request Headers
    ………………
    TEST_UNDERLINE: 123
    ………………

    目前我们还没有修改默认的配置,所以现在在响应打印的信息中,你应该看不到 TEST_UNDERLINE 这条新加的请求头信息。这就是因为默认情况下,underscores_in_headers 设置的是 off ,不启用下划线请求头,会将下划线请求头标记为非法的请求头,然后 ignore_invalid_headers 是 on ,自动忽略掉这些非常的请求头,不会将这些请求头继续向下转发。


    要测试非常简单。将 underscores_in_headers 设置为 on ,或者将 ignore_invalid_headers 设置为 off ,那么就都可以在 PHP 中看到打印出来的信息了。

    // http://192.168.56.88/1.php
    …………
    [HTTP_TEST_UNDERLINE] => 123
    …………

    总结

    东西其实不多,都是在围绕着这六个配置指令在进行测试。而且还有一些都不知道咋测,就像之前的文章中说过的,有接触过的小伙伴记得留言评论哦。


    请求的内容还有一部分,就是请求体以及请求限流,这两个我们合在一起下篇文章一起学习。


    参考文档:


    http://nginx.org/en/docs/http/ngx_http_core_module.html

    视频链接

    微信文章地址:https://mp.weixin.qq.com/s/Xb5v1vJyGf4r8xdEPeJCLw

    B站视频地址:https://www.bilibili.com/video/BV1P8411q79S/

    微信视频地址:https://mp.weixin.qq.com/s/B51i0Y8Mtqw4MZolDCM-Rw

    搜索
    关注