微信优化

weixinyouhua

nginx配置拦截请求怎么设置?nginx拦截请求配置方法

2026-08-20 06:18:19

nginx配置拦截请求的核心思路是先明确拦截对象,再选择匹配工具,最后落地为可维护的配置规则。就是通过locationiflimit_reqallow/deny等指令组合,把恶意IP、异常UA、高频访问和非法参数挡在业务逻辑之外,同时保证正常用户不受影响。

识别需要拦截的请求特征

拦截请求的第一步不是写配置,而是搞清楚哪些请求该拦,nginx的access.log是判断依据,用tail -f实时观察日志,结合awk '{print $1}' | sort | uniq -c | sort -rn统计IP出现频率,能快速定位异常来源。

常见需要拦截的请求类型包括:

  • 单一IP在短时间内发起大量请求,比如每秒几十次以上
  • 请求路径包含/wp-admin/admin.env..%2f等敏感特征
  • User-Agent为空或包含curlpython-requests扫描器等标记
  • Referer为空且访问的是静态资源接口,或者来源域名明显是垃圾流量
  • 请求参数中携带union select<script>等SQL注入或XSS特征

业内专家指出,多数攻击流量在到达业务代码之前就会暴露在HTTP头部和请求路径中,nginx层面做拦截刚好能兜住这一层。

从日志里找出攻击IP

先用grep配合正则把可疑请求筛出来,比如查看今天日志里所有包含/admin的请求:

grep "admin" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

如果某个IP的请求次数明显异常,比如达到了数百次甚至上千次,就可以把它列入拦截名单,注意区分正常爬虫和恶意爬虫,百度和谷歌的爬虫UA有固定格式,且会遵循robots协议,通常不需要拦截。

nginx配置拦截请求IP的三种姿势

拦截IP是常见需求,但不同场景匹配不同做法。

直接deny单个IP或IP段

ngx_http_access_module模块是内置基础能力,几乎零开销,在server块内直接写:

server {
    listen 80;
    server_name example.com;
    deny 192.168.1.10;
    deny 203.0.113.0/24;
    allow all;
}

allow all必须放在deny之后,否则顺序生效的匹配规则会把前面的deny覆盖掉,这个方案适合IP数量少、变化频率低的场景,比如屏蔽某个机房出口或内部测试IP。

nginx配置拦截请求怎么设置?nginx拦截请求配置方法

用map变量集中管理封禁名单

IP数量一多,散落在各个server块里就难维护了,用map做一个集中名单:

http {
    map $remote_addr $blocked_ip {
        default 0;
        192.168.1.10 1;
        203.0.113.0/24 1;
        198.51.100.7 1;
    }
    server {
        if ($blocked_ip = 1) {
            return 403;
        }
    }
}

这种方式把管理逻辑收敛到一处,新增IP只改map段,不用动业务配置,IP段支持CIDR格式,对于封禁某个云厂商的完整网段尤其方便。

针对UA或Referer的拦截

很多扫描器不改UA,直接用默认的爬虫标识,利用map同样能实现UA特征匹配:

map $http_user_agent $bad_ua {
    default 0;
    ~curl 1;
    ~python-requests 1;
    ~scrapy 1;
    ~sqlmap 1;
}
server {
    if ($bad_ua = 1) {
        return 403;
    }
}

表示不区分大小写的正则匹配,同样的思路可以扩展到Referer过滤,比如拦截某些垃圾外链带来的流量。

nginx限制请求频率防止恶意刷接口

IP封禁挡不住换IP的攻击,但限流可以控制整体速率。ngx_http_limit_req_module模块采用漏桶算法,对突发请求做平滑处理。

定义限流区域和速率

http块里配置:

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
}

这行配置的含义是:以客户端IP为key,开辟10MB共享内存区域,平均速率限制为每秒5个请求,10MB大约能存储16万个IP状态,常规业务足够用。

在location中启用限流

location /api/ {
    limit_req zone=api_limit burst=10 nodelay;
    proxy_pass http://backend;
}

burst是允许的突发队列长度,nodelay表示排队中的请求不额外延迟,直接快速处理,这样配合的效果是:平均每秒不超过5个请求,但允许单秒内最多有15个请求瞬间到达,超过的直接返回503。

如果需要区分接口权重,比如登录接口比查询接口限流更严格,可以定义多个limit_req_zone,在不同location引用不同的zone。

返回429而不是503

业界对限流响应的共识是返回429 Too Many Requests,比503更能准确传达语义,用limit_req_status

nginx配置拦截请求怎么设置?nginx拦截请求配置方法

指令修改:

location /api/ {
    limit_req zone=api_limit burst=5;
    limit_req_status 429;
    proxy_pass http://backend;
}

同时建议配合error_page返回一个简单的JSON提示:

error_page 429 /429.json;
location = /429.json {
    default_type application/json;
    return 200 '{"code":429,"msg":"请求过于频繁,请稍后再试"}';
}

拦截非法请求路径和参数

有些请求的特征藏在路径或参数里,用location + 正则就能精准匹配。

屏蔽敏感文件访问

location ~ \.(env|git|svn|bak|sql)$ {
    deny all;
    return 404;
}

不要直接返回403,返回404可以把文件是否存在的信息隐藏掉,对于.git目录被扫描的情况,这个返回404的策略很有效。

拦截带攻击特征的参数

if配合正则检查$query_string

if ($query_string ~ "($|%24).\(|union.select|insert.into|delete.from") {
    return 403;
}

这段正则覆盖了常见的SQL注入特征,需要注意,iflocation内使用时行为有历史坑,建议保持条件简单,并且在nginx -t测试通过后再上线。

处理被拦截请求的后续操作

拦截不只是返回一个状态码,合理的后续处理能提升防护效果。

记录拦截日志

nginx默认把所有请求写入access.log,拦截请求混在里面不方便审计,更好的做法是单独分流:

server {
    location /api/ {
        limit_req zone=api_limit burst=10;
        if ($blocked_ip = 1) {
            access_log /var/log/nginx/blocked.log;
            return 403;
        }
        access_log /var/log/nginx/access.log;
    }
}

这样被拦截的请求会单独落到blocked.log,方便后续分析攻击来源和频次。

搭配fail2ban自动化封禁

nginx处理不了的动态攻击,比如打一枪换一个IP的行为,可以配合fail2ban,在nginx配置里修改错误日志格式:

log_format blocked '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent"';

然后配置fail2ban去匹配

nginx配置拦截请求怎么设置?nginx拦截请求配置方法

blocked.log中高频出现的IP,自动添加iptables规则封禁,这个组合能把大量人工操作自动化。

配置验证与常见误区

配置写完后,用nginx -t检查语法正确性,然后nginx -s reload平滑重载,注意,reload不会断开现有连接,是零中断生效的。

顺序匹配带来的坑

nginx的deny/allow是顺序匹配的,先匹配到的生效,配置里如果写了allow all再写deny,那deny永远失效,同理,location内部匹配规则也是先到先得。

不要用if做复杂逻辑

iflocation内使用时是rewrite模块的一部分,写法不当会产生意想不到的问题,比如if内使用proxy_pass会报错,改写return才安全。行业共识是尽量少用if,能用map和location表达的场景就不要上if

拦截后要留出正常流量通道

比如CDN回源IP和真实用户IP混在一起,直接按$remote_addr限流会误伤,这时候需要通过set_real_ip_from配置让nginx识别用户真实IP,再针对真实来源做限制。

nginx配置拦截请求常见问题

nginx拦截请求返回403后如何知道是哪个规则命中的?

server块中配置日志格式增加$server_name$request_uri字段,然后按状态码筛选日志,更直接的办法是用curl -I -H "User-Agent: xx"模拟请求,配合nginx -tnginx -T查看当前生效的完整配置,逐步排查。

封禁IP和限流能否同时用?

能,在server块用deny做黑名单,在locationlimit_req做频控,两者是独立的模块链路,互不干扰,先过黑白名单,再过限流,最后进入代理逻辑。

nginx限流会误伤同一出口IP下的正常用户吗?

在办公网或学校等NAT环境下,确实存在这个可能,缓解方式是把limit_req_zone的key从$binary_remote_addr改为$http_x_forwarded_for,并配合real_ip_header信任代理服务器传递的真实IP,同时结合Cookie或Token维度做补充限制。

nginx拦截请求的投入产出比很高,大部分恶意流量在HTTP层就能被过滤掉,配置本身不复杂,重在维护规则文件的整洁和明确识别对象,从日志中定期更新拦截特征,配合自动化封禁工具,即可形成一个持续收敛攻击面的闭环。

相关文章

2024年,SaaS软件行业碰到获客难、增长慢等问题吗?

我们努力让每一次邂逅总能超越期待