$request_length 是 Nginx 内置变量,表示客户端发送的原始请求总字节数(含请求行、头及体),接收完毕即生效且不受 Content-Encoding 影响,可精准识别大载荷攻击;配合日志告警或 if 拦截实现轻量级实时防御,并需协同 client_header_timeout、client_body_timeout 等参数增强鲁棒性。

直接用 $request_length 变量做实时载荷长度监控,是发现和拦截大载荷拒绝服务攻击(如 Slow Body、恶意超长 POST、Gzip 炸弹前置请求等)最轻量也最有效的网关层手段之一。它不依赖后端解析,也不需要解压或反序列化,仅在 Nginx 接收到完整 HTTP 请求头+请求体后、转发前就可获取总字节数,天然适合做第一道防线。
为什么 $request_length 能有效识别大载荷攻击
$request_length 是 Nginx 内置变量,表示当前请求从客户端接收到的原始字节数(含请求行、所有头部、以及请求体)。它在请求接收完毕后立即可用,且不受 Content-Encoding 影响——哪怕请求体是 gzip 压缩的,这个值反映的是压缩后的网络传输长度,而非解压后大小。这意味着:
- 攻击者发送一个伪造的
Content-Length: 2147483647+ 实际只发几个字节,$request_length仍只计真实接收量,不会被欺骗; - 若攻击者真发送了数百 MB 的原始载荷(例如构造超大 JSON 或表单数据),该变量会立刻暴露异常值;
- 配合日志与监控,可快速区分正常大文件上传(如用户传 50MB 视频)和恶意填充(如 100MB 随机字符 POST)。
在 Nginx 中基于 request_length 设置拦截规则
不推荐仅靠 client_max_body_size(它只限制请求体,不包含头部),而应结合 $request_length 做更细粒度控制。常用方式有两类:
-
日志标记 + 异常告警:在 access_log 中记录该变量,例如:
log_format attack_log '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length';
再通过 ELK 或 Prometheus + Grafana 对$request_length > 10_000_000(即 10MB)的请求做高频统计与告警。 -
实时拦截 + 返回 413:使用
map+limit_req或if(慎用)实现动态拦截。推荐方案是:map $request_length $block_large_req {<br> default 0;<br> ~^[1-9][0-9]{7,} 1; # 匹配 ≥10MB 的数字<br>}
然后在 location 中:if ($block_large_req) { return 413; }
需配合的关键配置项
单独依赖 $request_length 不够稳健,必须与以下参数协同生效:
-
client_header_timeout 20s;:防止 Slow Headers 攻击中长时间发头不结束; -
client_body_timeout 30s;:限制请求体传输超时,对抗 Slow Body 类攻击; -
client_max_body_size 100m;:虽不如$request_length灵活,但仍是必要兜底,避免后端误处理; -
large_client_header_buffers 4 16k;:防超长恶意 header 占满缓冲区。
注意边界与误拦场景
真实业务中存在合理的大载荷请求,例如:
- API 批量导入(CSV/JSON 数十 MB);
- 移动端上传带元数据的高清图片或视频;
- Webhook 携带完整日志快照。
因此建议按路径精细化控制:对 /api/v1/upload 放宽至 200MB,而对 /login、/search 等接口统一限制在 1MB 以内。可通过 location ^~ /api/ 和 location = /login 分别设置不同 map 规则或 client_max_body_size 值,避免一刀切。

















