Nginx 请求限制实战:请求体大小与超时防滥用配置

适用场景

站点出现磁盘被大文件上传塞满、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_timeoutclient_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_filesizepost_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 同步,配合自定义错误页避免信息泄露。建议每次新业务上线时同步评审该业务的请求体与超时需求,让限制策略随业务演进持续收敛。