nginx限制某个IP同一时间段的访问次数

本文详细介绍了如何利用Nginx的HttpLimitReqModul和HttpLimitZoneModule配置来限制IP在同一时间段的访问次数,有效防止CC攻击。包括实例演示、白名单设置及与ab工具的测试方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何设置能限制某个IP某一时间段的访问次数是一个让人头疼的问题,特别面对恶意的ddos攻击的时候。其中CC攻击(Challenge Collapsar)是DDOS(分布式拒绝服务)的一种,也是一种常见的网站攻击方法,攻击者通过代理服务器或者肉鸡向向受害主机不停地发大量数据包,造成对方服务器资源耗尽,一直到宕机崩溃。

cc攻击一般就是使用有限的ip数对服务器频繁发送数据来达到攻击的目的,nginx可以通过HttpLimitReqModul和HttpLimitZoneModule配置来限制ip在同一时间段的访问次数来防cc攻击。

HttpLimitReqModul用来限制连单位时间内连接数的模块,使用limit_req_zone和limit_req指令配合使用来达到限制。一旦并发连接超过指定数量,就会返回503错误。

HttpLimitConnModul用来限制单个ip的并发连接数,使用limit_zone和limit_conn指令

这两个模块的区别前一个是对一段时间内的连接数限制,后者是对同一时刻的连接数限制

HttpLimitReqModul 限制某一段时间内同一ip访问数实例

http{
    ...

    #定义一个名为allips的limit_req_zone用来存储session,大小是10M内存,
    #以$binary_remote_addr 为key,限制平均每秒的请求为20个,
    #1M能存储16000个状态,rete的值必须为整数,
    #如果限制两秒钟一个请求,可以设置成30r/m

    limit_req_zone $binary_remote_addr zone=allips:10m rate=20r/s;
    ...
    server{
        ...
        location {
            ...

            #限制每ip每秒不超过20个请求,漏桶数burst为5
            #brust的意思就是,如果第1秒、2,3,4秒请求为19个,
            #第5秒的请求为25个是被允许的。
            #但是如果你第1秒就25个请求,第2秒超过20的请求返回503错误。
            #nodelay,如果不设置该选项,严格使用平均速率限制请求数,
            #第1秒25个请求时,5个请求放到第2秒执行,
            #设置nodelay,25个请求将在第1秒执行。

            limit_req zone=allips burst=5 nodelay;
            ...
        }
        ...
    }
    ...
}

 

HttpLimitZoneModule 限制并发连接数实例

limit_zone只能定义在http作用域,limit_conn可以定义在http server location作用域

http{
    ...

    #定义一个名为one的limit_zone,大小10M内存来存储session,
    #以$binary_remote_addr 为key
    #nginx 1.18以后用limit_conn_zone替换了limit_conn
    #且只能放在http作用域
    limit_conn_zone   one  $binary_remote_addr  10m;  
    ...
    server{
        ...
        location {
            ...
           limit_conn one 20;          #连接数限制

           #带宽限制,对单个连接限数,如果一个ip两个连接,就是500x2k
           limit_rate 500k;            

            ...
        }
        ...
    }
    ...
}

 

nginx白名单设置

以上配置会对所有的ip都进行限制,有些时候我们不希望对搜索引擎的蜘蛛或者自己测试ip进行限制,
对于特定的白名单ip我们可以借助geo指令实现。
1.

http{
     geo $limited{
        default 1;
        #google 
        64.233.160.0/19 0;
        65.52.0.0/14 0;
        66.102.0.0/20 0;
        66.249.64.0/19 0;
        72.14.192.0/18 0;
        74.125.0.0/16 0;
        209.85.128.0/17 0;
        216.239.32.0/19 0;
        #M$
        64.4.0.0/18 0;
        157.60.0.0/16 0;
        157.54.0.0/15 0;
        157.56.0.0/14 0;
        207.46.0.0/16 0;
        207.68.192.0/20 0;
        207.68.128.0/18 0;
        #yahoo
        8.12.144.0/24 0;
        66.196.64.0/18 0;
        66.228.160.0/19 0;
        67.195.0.0/16 0;
        74.6.0.0/16 0;
        68.142.192.0/18 0;
        72.30.0.0/16 0;
        209.191.64.0/18 0;
        #My IPs
        127.0.0.1/32 0;
        123.456.0.0/28 0; #example for your server CIDR
    }

geo指令定义了一个白名单$limited变量,默认值为1,如果客户端ip在上面的范围内,$limited的值为0

2.使用map指令映射搜索引擎客户端的ip为空串,如果不是搜索引擎就显示本身真是的ip,这样搜索引擎ip就不能存到limit_req_zone内存session中,所以不会限制搜索引擎的ip访问

map $limited $limit {
1 $binary_remote_addr;
0 "";
}

3.设置limit_req_zone和limit_req
limit_req_zone $limit zone=foo:1m rate=10r/m;

limit_req zone=foo burst=5;

最后我们使用abphp-fpm的方式,对上面的方法效果实际测试下

例1:限制只允许一分钟内只允许一个ip访问60次配置,也就是平均每秒1次
首先我们准备一个php脚本放在根目录下$document_root
test.php

for( $i=0; $i < 1000; $i++)
echo 'Hello World';
?>

nginx配置增加limit_req_zone 和 limit_req

http{
    ...
    limit_req_zone $binary_remote_addr zone=allips:10m rate=60r/m;
    ...
    server{
        ...
        location {
            ...
            limit_req zone=allips;
            ...
        }
        ...
    }
    ...
}

ab -n 5 -c 1 http://www.weizhang.org/test.php

118.144.94.193 - - [22/Dec/2012:06:27:06 +0000] "GET /test.php HTTP/1.0" 200 11000 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:06:27:06 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:06:27:07 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:06:27:07 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:06:27:07 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"

未设置brust和nodelay可以看到该配置只允许每秒访问1次,超出的请求返回503错误

http{
    ...
    limit_req_zone $binary_remote_addr zone=allips:10m rate=60r/m;
    ...
    server{
        ...
        location {
            ...
            limit_req zone=allips burst=1 nodelay;
            ...
        }
        ...
    }
    ...
}

ab -n 5 -c 1 http://www.weizhang.org/test.php

118.144.94.193 - - [22/Dec/2012:07:01:00 +0000] "GET /test.php HTTP/1.0" 200 11000 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:07:01:00 +0000] "GET /test.php HTTP/1.0" 200 11000 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:07:01:01 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:07:01:01 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"
118.144.94.193 - - [22/Dec/2012:07:01:01 +0000] "GET /test.php HTTP/1.0" 503 537 "-" "ApacheBench/2.3"

设置brust=1和nodelay后允许第1秒处理两个请求。

<think>嗯,用户想要了解如何统计Nginx日志中URL接口的请求次数,按照去重后的结果正序显示。首先,我需要回忆一下Nginx日志的格式是什么样的。通常,Nginx访问日志默认格式会包含客户端的IP、时间、请求方法、URL路径、HTTP状态码等等。比如一条日志可能长这样: 127.0.0.1 - - [10/Oct/2023:14:55:36 +0800] "GET /api/user?id=123 HTTP/1.1" 200 612 用户的需求是统计这些日志中的URL请求次数,去重后按正序排列。这里的“去重”可能是指每个唯一的URL只统计一次,但更可能的是指统计每个不同URL的请求次数,然后按次数升序排列。需要确认一下用户的意图,但根据常见情况,应该是后者。 接下来,我需要考虑如何解析日志文件并提取URL。通常,可以使用命令行工具如awk来处理。例如,使用awk提取URL字段。在默认的日志格式中,URL通常是第7个字段,但这也取决于日志的具体格式。用户可能需要先确认自己的日志格式,用命令head查看日志结构,确定URL的位置。 假设URL在第7个字段,那么可以使用以下命令来统计: awk '{print $7}' access.log | sort | uniq -c | sort -n 这里,awk '{print $7}' 提取URL,sort排序以便uniq -c能统计每个URL出现的次数,然后sort -n按次数升序排列。不过,如果URL包含查询参数,比如/api/user?id=123和/api/user?id=456会被视为不同的URL,是否需要去除查询参数?用户可能需要根据实际情况决定是否要去掉问号后面的部分。可以使用sed或awk进一步处理,例如用awk的sub函数去除?后的内容: awk '{split($7, a, "?"); print a[1]}' access.log | sort | uniq -c | sort -n 这样就能将同一路径不同参数的URL合并统计。但用户的问题中提到的“去重后正序显示”可能需要明确是否要去除参数。需要提醒用户注意这一点。 另外,可能要考虑不同的请求方法(GET、POST等),如果用户需要区分方法,则应在提取URL时同时包含方法,或者根据需求调整。例如: awk '{print $6, $7}' access.log | sort | uniq -c | sort -n 这里的$6是请求方法,$7是URL,这样不同方法的同一URL会被分开统计。 另外,日志文件可能有多个,比如access.log.1, access.log.2.gz等,用户可能需要使用zcat来处理压缩文件,或者使用cat合并多个日志文件。 总结步骤应该是: 1. 确认日志格式,确定URL所在的字段位置。 2. 提取URL字段,可能处理查询参数。 3. 使用sort和uniq -c统计次数。 4. 再次sort按次数排序,正序使用-n,倒序使用-nr。 5. 可能需要处理多个日志文件,使用cat或zcat。 然后,将这些步骤转化为具体的命令,并给出示例。同时,提醒用户注意URL处理中的细节,比如参数、方法等,以及可能的日志文件位置和权限问题。 可能的错误点:用户可能不知道如何确定URL字段的位置,或者日志格式自定义导致字段位置不同。需要建议用户先查看日志样例,例如使用head -n 1 access.log来确认。 另外,如果用户需要更复杂的处理,比如统计特定时间段的请求,可能需要结合日期处理,但根据问题描述,用户可能暂时不需要,所以暂时不涉及。 最后,确保命令的正确性,并给出示例输出,让用户能够理解结果的形式。</think>统计Nginx日志中URL接口请求次数可按以下步骤操作: --- ### 一、理解日志格式 默认Nginx日志格式示例: $$127.0.0.1 - - [10/Oct/2023:14:55:36 +0800] "GET /api/user?id=123 HTTP/1.1" 200 612$$ 其中`$request`字段包含`方法 + URL + 协议`,通常为日志的第6、7、8字段(取决于日志配置) --- ### 二、基础统计命令 #### 1. 提取URL并统计 ```bash # 提取第7字段(URL路径),统计出现次数 awk '{print $7}' access.log | sort | uniq -c | sort -n ``` #### 2. 处理带参数的URL(可选) 若需忽略查询参数`?`后的内容: ```bash awk '{split($7, a, "?"); print a[1]}' access.log | sort | uniq -c | sort -n ``` --- ### 三、完整命令解释 1. **`awk '{print $7}'`** 提取日志第7个字段(URL路径) 2. **`sort`** 将URL排序(`uniq -c`的前置条件) 3. **`uniq -c`** 统计重复项并显示计数 4. **`sort -n`** 按请求次数升序排列(`-nr`则为降序) --- ### 四、增强版命令 #### 1. 包含请求方法 ```bash awk '{print $6 " " $7}' access.log | sort | uniq -c | sort -n ``` #### 2. 处理多个日志文件 ```bash cat access.log*.gz | zcat | awk '{print $7}' | sort | uniq -c | sort -n ``` --- ### 五、结果示例 ```text 5 /api/login 12 /static/css/style.css 20 /api/user/list ``` --- ### 六、注意事项 1. **字段位置验证** 先用`head -n 1 access.log`确认URL所在字段位置 2. **时间范围筛选** 如需按时间段统计,可结合`grep`过滤时间: ```bash awk '/10\/Oct\/2023:14:/{print $7}' access.log | sort | uniq -c ``` 3. **性能优化** 处理大文件时建议先`split`分割日志或使用`LC_ALL=C`加速: ```bash LC_ALL=C awk '{print $7}' access.log | LC_ALL=C sort | LC_ALL=C uniq -c ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值