适用场景
站点出现磁盘被大文件上传塞满、worker 连接被慢速请求占满、PHP 进程被超大 POST 打挂等问题时,需要通过 Nginx 请求限制收口。请求体大小限制与超时控制是 Nginx 防滥用的第一道闸门:不设防的服务器,任何访客都能一次性 POST 数 GB 数据耗尽磁盘,或用极慢速率占住连接制造资源饥饿。本文给出请求体与超时参数的完整配置方案。
前置条件
- Nginx 1.10 及以上,拥有 root 权限
- 后端为 PHP-FPM、Tomcat 或反向代理的应用服务
- 已梳理业务的最大上传需求(如附件上传上限 50MB)
原理说明
Nginx 对客户端请求的分段控制由两组参数构成。请求体限制:client_max_body_size 定义请求体上限,超限直接返回 413;client_body_buffer_size 控制请求体在内存中的缓冲,超出部分落盘临时文件。超时限制:client_body_timeout 与 client_header_timeout 限制客户端两次读事件之间的间隔,keepalive_timeout 控制空闲连接保持时间,send_timeout 限制向客户端发送响应的间隔。慢速攻击(如 Slowloris 变体)正是利用”不超时”这一默认宽容,持续发送残缺请求占住 worker——把超时收紧到 10 秒级即可大幅压缩攻击收益。需注意 PHP 侧 upload_max_filesize/post_max_size 必须与 Nginx 同步,否则出现 413 与应用报错不一致的问题。
操作步骤
第一步:全局限制配置
在 http {} 层写入基础限制(编辑 /etc/nginx/nginx.conf):
http {
# 请求体全局上限 1MB(无上传需求的站默认收紧)
client_max_body_size 1m;
# 请求体内存缓冲 128K,超出落盘
client_body_buffer_size 128k;
# 两次读事件最大间隔 10 秒,防慢速占用
client_body_timeout 10s;
client_header_timeout 10s;
# 空闲 keepalive 连接 15 秒断开
keepalive_timeout 15s;
# 响应发送间隔上限 30 秒
send_timeout 30s;
# ... 其余 http 配置
}
第二步:上传目录单独放宽
有上传需求的业务在对应 location 内单独覆盖,避免全局放宽:
server {
listen 80;
server_name example.com;
# 普通页面继承全局 1m 限制
# 上传接口单独放宽至 50m
location /api/upload {
client_max_body_size 50m;
client_body_timeout 60s; # 大文件传输允许更长间隔
proxy_pass http://127.0.0.1:8080;
}
}
关键点:client_max_body_size 可在 http/server/location 三层配置,内层覆盖外层;超时同理。放宽必须精确到最小范围——只放开上传接口,不放开整站。
第三步:PHP 侧参数同步
php-fpm 环境下,编辑 php.ini 保持与 Nginx 一致:
upload_max_filesize = 50M
post_max_size = 52M
memory_limit = 128M
注意 post_max_size 要略大于 upload_max_filesize(请求体含 multipart 开销),改完执行 systemctl reload php-fpm。若使用反向代理转发,还需补上后端读取超时:
location / {
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://127.0.0.1:8080;
}
第四步:自定义 413 错误页
默认 413 页面会暴露 Nginx 版本信息,且部分浏览器对 413 的展示不友好,建议替换:
server {
error_page 413 /413.html;
location = /413.html {
internal;
return 200 '{"code":413,"msg":"请求体超过大小限制"}';
default_type application/json;
}
}
配置验证
# 1. 语法检查并重载
nginx -t && nginx -s reload
# 2. 超限请求应返回 413
dd if=/dev/zero of=/tmp/test10m bs=1M count=10
curl -s -o /dev/null -w "%{http_code}\n" -X POST --data-binary @/tmp/test10m http://example.com/
# 3. 正常请求不受影响
curl -s -o /dev/null -w "%{http_code}\n" http://example.com/
# 4. 确认 php 侧参数一致
php -i | grep -E "upload_max_filesize|post_max_size"
第 2 步预期输出 413,第 3 步预期 200。若第 2 步返回 200,说明 location 层放宽范围过大,回头检查配置作用域。
常见问题
FAQ 1:上传大文件报 413,但业务确实需要传大附件
不要直接调大全局 client_max_body_size。正确做法是定位到上传接口的 location(如 location /api/upload)单独放宽,并同步修改 php.ini 的 upload_max_filesize 与 post_max_size 后重载 php-fpm。全局保持 1m 收紧状态,只对最小路径放宽。
FAQ 2:客户端 $_POST 为空但 Nginx 没有报 413
典型的 PHP 侧限制小于 Nginx 侧:请求体超过了 post_max_size,PHP 直接丢弃 POST 数据但不报错。检查 php -i | grep post_max_size,将其调到与 client_max_body_size 匹配(略大 2MB 左右)即可恢复。
FAQ 3:413 响应到达时连接被直接重置,自定义错误页看不到
Nginx 检测到 Content-Length 超限后会先读完已到达的缓冲再响应,客户端提前断开导致错误页无法送达,这是正常行为,不影响拦截效果。若前端依赖 JSON 错误码,建议在前端请求侧先做大小校验,服务端 413 作为兜底。
总结
Nginx 请求限制是成本最低、收益明确的防滥用配置:请求体上限挡住大包灌盘与内存耗尽,超时参数压缩慢速攻击的收益空间,三层作用域让”全局收紧、局部放宽”可以精确到接口级。执行要点是全局默认从紧(1m/10s)、上传路径精确放宽、PHP 参数与 Nginx 同步,配合自定义错误页避免信息泄露。建议每次新业务上线时同步评审该业务的请求体与超时需求,让限制策略随业务演进持续收敛。