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

2023-09-07 09:06:54 浏览数 (1)

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

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

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

请求头

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

client_header_buffer_size

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

代码语言:javascript复制
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(容量) 。

代码语言:javascript复制
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 。

代码语言:javascript复制
// 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 的错误。

代码语言:javascript复制
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 错误。或者直接一个大的请求头。下面这样的请求也会报错。

代码语言:javascript复制
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 ,上面的请求都可以正常访问了,接下来我们测另外两种情况。

代码语言:javascript复制
large_client_header_buffers 2 512;

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

代码语言:javascript复制
GET http://192.168.56.88/
....
LongHeader: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde
....

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

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

代码语言:javascript复制
GET http://192.168.56.88/?bigparams=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr

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

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

client_header_timeout

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

代码语言:javascript复制
client_header_timeout time;

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

max_ranges

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

代码语言:javascript复制
max_ranges number;

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

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

代码语言:javascript复制
GET http://192.168.56.88/

…………
Range: bytes=0-100,100-200,200-300
…………

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

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

代码语言:javascript复制
// nginx.conf
……
server {
 max_ranges 0;
 ……
}
……

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

代码语言:javascript复制
max_ranges 2;

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

underscores_in_headers

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

代码语言:javascript复制
underscores_in_headers on | off;

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

ignore_invalid_headers

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

代码语言:javascript复制
ignore_invalid_headers on | off;

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

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

代码语言:javascript复制
// 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 文件。

代码语言:javascript复制
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 中看到打印出来的信息了。

代码语言:javascript复制
// http://192.168.56.88/1.php
…………
[HTTP_TEST_UNDERLINE] => 123
…………

总结

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

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

参考文档:

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

0 人点赞