一、前言:为什么需要动态封禁 IP?

在服务器运维中,恶意 IP 攻击(如 CC 攻击、暴力破解、接口刷量)是高频问题。据统计,未防护的服务器日均遭受 15 + 次恶意 IP 请求,可能导致 CPU 利用率飙升至 90% 以上、接口响应延迟超 5 秒,甚至服务宕机。

Nginx 作为主流反向代理服务器,无需依赖第三方软件,即可通过内置指令与扩展模块实现动态封禁 IP。本文从技术原理、实操配置、场景适配三个维度,详解 3 种核心方案,覆盖临时应急、长期防护、智能拦截需求,附完整代码与问题排查指南。

二、核心方案一:基于 Nginx 内置指令的临时封禁(适用于应急场景)

1. 技术原理

Nginx 的deny指令用于拒绝指定 IP 的访问请求,配置后 Nginx 在接收请求时会优先校验客户端 IP,若命中deny规则则直接返回 403 Forbidden,不进入后续业务逻辑。该方案无需依赖任何模块,配置简单,1 分钟即可生效。

2. 完整配置步骤
(1)定位 Nginx 配置文件

不同安装方式的配置文件路径不同,需先确认路径:

  • yum/apt 安装:/etc/nginx/nginx.conf(主配置)、/etc/nginx/conf.d/(站点配置)
  • 源码编译安装:/usr/local/nginx/conf/nginx.conf
  • 验证路径:通过nginx -t命令查看配置文件路径,示例输出:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok

nginx: configuration file /etc/nginx/nginx.conf test is successful

(2)添加封禁规则

根据需求选择全局封禁(所有站点生效)或站点级封禁(仅当前站点生效):

  • 全局封禁:在nginx.conf的http节点下添加deny指令:

http {

# 全局封禁单个IP

deny 192.168.1.100;

# 全局封禁多个IP

deny 203.0.113.8;

deny 10.0.0.5;

# 其他配置(如include站点配置、日志配置等)

include /etc/nginx/conf.d/*.conf;

access_log /var/log/nginx/access.log main;

}

  • 站点级封禁:在具体站点配置(如/etc/nginx/conf.d/api.conf)的server节点下添加:

server {

listen 80;

server_name api.yourdomain.com;

# 仅当前站点封禁IP

deny 192.168.1.100;

location / {

proxy_pass http://127.0.0.1:8080; # 业务服务地址

proxy_set_header Host $host;

}

}

(3)验证与生效配置
  • 语法校验:修改配置后必须先测试,避免语法错误导致服务中断:

nginx -t

  • 刷新配置:无需重启 Nginx,执行reload即可让配置生效:

nginx -s reload

3. 效果验证与解封操作
  • 验证封禁:使用curl命令模拟被封 IP 访问,返回 403 即成功:

# 替换为被封IP和目标域名

curl -x 192.168.1.100:80 http://api.yourdomain.com

# 输出:<html><head><title>403 Forbidden</title></head><body>...</body></html>

  • IP 解封:删除对应的deny指令,执行nginx -t && nginx -s reload即可。
4. 局限性分析
  • 仅支持单个 IP 封禁,不支持网段(如192.168.1.0/24);
  • 当封禁 IP 数量超过 10 个时,主配置文件会变得杂乱,维护成本高;
  • 不支持自动封禁,需人工监控日志并手动添加规则。

三、核心方案二:基于独立黑名单文件的批量封禁(适用于长期防护)

1. 技术原理

通过include指令将封禁 IP 规则剥离到独立文件(如ip_blacklist.conf),主配置仅需引用该文件,实现 “配置分离”。该方案支持批量管理 IP,便于定期维护,且不污染主配置文件。

2. 完整配置步骤
(1)创建黑名单文件

在 Nginx 配置目录下新建ip_blacklist.conf,支持单个 IP、网段封禁,可添加注释记录封禁原因与时间:


# /etc/nginx/conf.d/ip_blacklist.conf

# 格式:deny IP/网段; # 封禁原因 时间

deny 192.168.1.100; # CC攻击 2025-11-01

deny 203.0.113.0/24; # 批量刷接口 2025-11-01(网段封禁)

deny 10.0.0.5; # 暴力破解后台 2025-11-02

(2)主配置引用黑名单

在nginx.conf的http节点或站点配置的server节点下引用文件:


http {

# 全局引用黑名单(所有站点生效)

include /etc/nginx/conf.d/ip_blacklist.conf;

# 其他配置...

server {

listen 80;

server_name api.yourdomain.com;

# 站点级引用(仅当前站点生效,二选一即可)

# include /etc/nginx/conf.d/ip_blacklist.conf;

}

}

(3)生效配置

同方案一,执行nginx -t && nginx -s reload。

3. 维护技巧
  • 新增 IP:直接在ip_blacklist.conf末尾追加deny指令,无需修改主配置;
  • 批量导入:若有大量 IP 需封禁(如 100+),可通过 Excel 生成deny IP;格式,复制到黑名单文件:

# Excel公式:=CONCATENATE("deny ",A1,"; # 封禁原因 ",B1)

# A列存IP,B列存原因,生成后复制到文件

  • 定期清理:每周通过grep命令筛选 30 天内无攻击记录的 IP,从黑名单中删除:

# 查看IP近30天是否有请求(替换为目标IP)

grep "192.168.1.100" /var/log/nginx/access.log | grep -E "$(date -d '30 days ago' +%Y/%m/%d)" -A 10 -B 10

4. 优势与适用场景
  • 支持网段封禁,适配批量攻击场景;
  • 配置分离,主配置文件简洁,维护成本低;
  • 适合长期防护,如封禁已知恶意 IP 段、历史攻击 IP。

四、核心方案三:基于 Lua 脚本的自动封禁(适用于智能拦截)

1. 技术原理

借助ngx_http_lua_module模块,通过 Lua 脚本实时统计每个 IP 的请求次数,当请求数超过预设阈值(如 100 次 / 分钟)时,自动返回 403 并记录日志。该方案支持自动拦截高频恶意请求,无需人工干预。

2. 前置准备:安装 Lua 模块
  • CentOS/RHEL:通过 yum 安装预编译模块:

yum install nginx-mod-http-lua -y

  • Ubuntu/Debian:安装nginx-extras(内置 Lua 模块):

apt update && apt install nginx-extras -y

  • 验证安装:执行nginx -V 2>&1 | grep lua,输出包含--with-http_lua_module即成功。
3. 完整配置步骤
(1)配置 Lua 共享内存

在nginx.conf的http节点下定义共享内存,用于存储 IP 请求计数(内存级存储,Nginx 重启后清空):


http {

# 定义共享内存:名称ip_limit_dict,大小10M(可存储10万+IP的计数)

lua_shared_dict ip_limit_dict 10m;

# 其他配置...

}

(2)编写 Lua 自动封禁脚本

在 Nginx 配置目录下新建lua文件夹,创建ip_auto_block.lua脚本,支持阈值配置、白名单、日志记录:


-- /etc/nginx/lua/ip_auto_block.lua

-- 1. 获取共享内存对象

local ip_dict = ngx.shared.ip_limit_dict

-- 2. 获取客户端IP(支持反向代理场景,若有CDN需调整为X-Forwarded-For)

local client_ip = ngx.var.remote_addr

-- 若使用CDN/反向代理,替换为:local client_ip = ngx.var.http_x_forwarded_for or ngx.var.remote_addr

if not client_ip then

ngx.exit(403) -- 无法获取IP时拒绝访问

return

end

-- 3. 配置白名单(避免误封正常IP/网段)

local whitelist = {

["192.168.1.200"] = true, -- 管理员IP

["10.0.0.0/24"] = true, -- 内网网段(支持CIDR)

["202.103.0.0/16"] = true -- 办公网网段

}

-- 4. 白名单校验(网段匹配逻辑)

local function is_in_whitelist(ip)

-- 单个IP匹配

if whitelist[ip] then

return true

end

-- 网段匹配(如10.0.0.0/24)

local ip_segs = {}

for seg in string.gmatch(ip, "%d+") do

table.insert(ip_segs, tonumber(seg))

end

for cidr, _ in pairs(whitelist) do

local cidr_ip, mask = string.match(cidr, "([%d.]+)/(%d+)")

if cidr_ip and mask then

local cidr_segs = {}

for seg in string.gmatch(cidr_ip, "%d+") do

table.insert(cidr_segs, tonumber(seg))

end

local mask_num = tonumber(mask)

local match = true

for i = 1, 4 do

local bit_start = (i-1)*8 + 1

local bit_end = math.min(i*8, mask_num)

if bit_end < bit_start then

break

end

local ip_bit = math.floor(ip_segs[i] / (2^(8 - (bit_end - bit_start + 1))))

local cidr_bit = math.floor(cidr_segs[i] / (2^(8 - (bit_end - bit_start + 1))))

if ip_bit ~= cidr_bit then

match = false

break

end

end

if match then

return true

end

end

end

return false

end

-- 5. 白名单IP直接放行

if is_in_whitelist(client_ip) then

return

end

-- 6. 配置封禁阈值(可根据业务调整)

local limit_count = 100 -- 单位时间内最大请求数

local limit_seconds = 60 -- 单位时间(秒)

-- 7. 统计IP请求次数

local current_count, err = ip_dict:get(client_ip)

if not current_count then

-- 首次请求:初始化计数,设置过期时间

ip_dict:set(client_ip, 1, limit_seconds)

else

-- 非首次请求:计数+1

current_count = current_count + 1

ip_dict:set(client_ip, current_count, limit_seconds)

-- 8. 超过阈值:记录日志并封禁

if current_count > limit_count then

-- 记录错误日志(路径:/var/log/nginx/error.log)

ngx.log(ngx.ERR, string.format("[Nginx自动封禁] IP: %s, 请求次数: %d, 阈值: %d", client_ip, current_count, limit_count))

-- 返回403,可自定义响应页面

ngx.status = 403

ngx.header["Content-Type"] = "text/html; charset=utf-8"

ngx.say("<h1>403 Forbidden</h1><p>您的IP因异常请求已被临时封禁," .. limit_seconds .. "秒后可恢复访问</p>")

ngx.exit(ngx.HTTP_FORBIDDEN)

end

end

(3)Nginx 引用 Lua 脚本

在需要防护的server或location节点下,通过access_by_lua_file指令引用脚本(该指令在请求进入业务逻辑前执行):


server {

listen 80;

server_name api.yourdomain.com;

# 对所有请求生效(放在server节点下)

access_by_lua_file /etc/nginx/lua/ip_auto_block.lua;

# 仅对接口请求生效(放在location节点下,二选一)

location /api/ {

access_by_lua_file /etc/nginx/lua/ip_auto_block.lua;

proxy_pass http://127.0.0.1:8080;

proxy_set_header Host $host;

}

}

(4)生效与测试
  • 生效配置:nginx -t && nginx -s reload
  • 模拟测试:使用ab工具模拟高频请求,验证自动封禁效果:

# 150次请求,10并发(超过100次阈值)

ab -n 150 -c 10 http://api.yourdomain.com/api/user/list

  • 验证结果:超过阈值后,访问返回自定义 403 页面,/var/log/nginx/error.log中出现封禁日志。
4. 性能优化建议
  • 共享内存大小:根据并发 IP 数量调整,公式:大小(M)= 并发IP数 × 0.1KB / 1024,如 10 万 IP 需 10M;
  • 阈值配置:静态资源(图片、CSS)设 200-300 次 / 分钟,接口服务设 50-100 次 / 分钟,登录接口设 10-20 次 / 分钟;
  • CDN 场景适配:若使用 CDN,需将client_ip获取逻辑改为ngx.var.http_x_forwarded_for,并确保 CDN 已透传真实 IP。

五、常见问题排查与解决方案

1. 配置后 Nginx 启动失败
  • 报错信息:nginx: [emerg] unknown directive "deny" in /etc/nginx/nginx.conf
    • 原因:deny指令需放在http、server或location节点下,位置错误;
    • 解决方案:调整deny指令位置,确保在合法节点内。
  • 报错信息:nginx: [emerg] open() "/etc/nginx/conf.d/ip_blacklist.conf" failed (2: No such file or directory)
    • 原因:黑名单文件路径错误;
    • 解决方案:通过find / -name "ip_blacklist.conf"确认路径,修正include指令。
2. 被封 IP 仍能访问
  • 原因 1:IP 在白名单中;
    • 解决方案:检查ip_auto_block.lua的whitelist配置,移除目标 IP。
  • 原因 2:使用 CDN / 反向代理,Nginx 获取的是代理 IP 而非真实 IP;
    • 解决方案:配置X-Forwarded-For头,修改client_ip获取逻辑。
3. Lua 脚本不生效
  • 原因 1:未安装ngx_http_lua_module模块;
    • 解决方案:重新安装模块,执行nginx -V验证。
  • 原因 2:共享内存名称与脚本不一致;
    • 解决方案:确保lua_shared_dict的名称(如ip_limit_dict)与脚本中ngx.shared.xxx一致。

六、方案选型与混合防护策略

1. 多维度方案对比表

对比维度

方案一:内置指令临时封禁

方案二:黑名单文件批量封禁

方案三:Lua 脚本自动封禁

核心适用场景

临时应急(1-5 个 IP)

长期防护(10+IP / 网段)

智能拦截(高频攻击 / CC)

支持 IP 类型

单个 IP

单个 IP、网段(CIDR)

单个 IP、网段(需脚本适配)

自动化程度

完全手动

半手动(需人工维护名单)

全自动(阈值触发)

性能开销

极低(Nginx 原生校验)

低(文件加载一次,内存缓存)

中(Lua 脚本实时计算)

维护成本

高(IP 多则配置杂乱)

低(配置分离,易维护)

中(需定期调整阈值)

依赖模块

ngx_http_lua_module

2. 场景化选型建议
  • 个人博客 / 小型站点:优先选择 “方案一 + 方案二” 组合,日常通过黑名单文件封禁已知恶意 IP,突发少量攻击时用内置指令临时应急,无需额外安装模块,运维成本低。
  • 中型接口服务(如电商 API):采用 “方案二 + 方案三” 混合防护,黑名单文件封禁历史攻击 IP 段,Lua 脚本自动拦截高频请求(如 1 分钟内请求超 100 次的 IP),兼顾安全性与自动化。
  • 大型分布式系统(如金融 / 政务平台):在 “方案二 + 方案三” 基础上,结合fail2ban工具(监控 Nginx 日志,自动添加恶意 IP 到黑名单),并通过 API 将封禁名单同步至所有 Nginx 节点,实现集群级防护。
3. 混合防护配置示例(中型接口服务)

http {

# 1. 配置Lua共享内存(方案三依赖)

lua_shared_dict ip_limit_dict 10m;

# 2. 引用黑名单文件(方案二)

include /etc/nginx/conf.d/ip_blacklist.conf;

server {

listen 80;

server_name api.yourdomain.com;

# 3. 启用Lua自动封禁(方案三)

access_by_lua_file /etc/nginx/lua/ip_auto_block.lua;

# 4. 业务配置

location /api/ {

proxy_pass http://127.0.0.1:8080;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}

# 5. 静态资源跳过Lua校验(降低性能开销)

location ~* \.(jpg|png|css|js)$ {

root /usr/share/nginx/html;

expires 1d; # 缓存静态资源,减少请求次数

}

}

}

七、高级扩展:从 “封禁” 到 “智能防护”

1. 结合fail2ban实现日志驱动的自动封禁

fail2ban是一款日志监控工具,可分析 Nginx 访问日志,当检测到特定攻击行为(如密码试错超 5 次、单 IP 请求超 200 次)时,自动将 IP 添加到 Nginx 黑名单文件,实现 “日志分析→自动加黑→定时解封” 闭环。

核心配置步骤

  • 安装 fail2ban

# CentOS

yum install fail2ban -y

# Ubuntu

apt install fail2ban -y

  • 配置 Nginx 日志监控规则(/etc/fail2ban/jail.d/nginx.conf):

[nginx-cc]

enabled = true

filter = nginx-cc

logpath = /var/log/nginx/access.log

maxretry = 100 # 10分钟内请求超100次触发封禁

findtime = 600 # 统计窗口(10分钟)

bantime = 3600 # 封禁时长(1小时)

action = iptables[name=NGINX-CC, port=http, protocol=tcp]

# 同时添加到Nginx黑名单文件

/bin/echo "deny <ip>; # fail2ban auto-ban $(date +%Y-%m-%d)" >> /etc/nginx/conf.d/ip_blacklist.conf

/usr/sbin/nginx -t && /usr/sbin/nginx -s reload

  • 启动 fail2ban

systemctl start fail2ban

systemctl enable fail2ban

2. 分布式场景:集群封禁名单同步

在多 Nginx 节点的分布式系统中,需确保各节点封禁名单一致,可通过以下两种方案实现:

  • 方案一:共享存储(如 NFS):将ip_blacklist.conf文件放在 NFS 共享目录,所有 Nginx 节点通过include引用共享目录下的文件,修改后所有节点实时生效。
  • 方案二:API 同步:开发封禁名单管理 API,当某节点添加 / 删除 IP 时,通过 API 通知其他节点更新本地ip_blacklist.conf并reload配置,适合跨机房部署场景。
3. 误封补救:手动解封与白名单优化
  • 手动解封:除了删除deny指令,可编写 Shell 脚本快速移除 IP(如./unban.sh 192.168.1.100):

#!/bin/bash

IP=$1

# 从黑名单文件中删除该IP的deny指令

sed -i "/deny $IP;/d" /etc/nginx/conf.d/ip_blacklist.conf

# 刷新Nginx配置

nginx -t && nginx -s reload

echo "IP $IP 已解封"

  • 白名单优化:定期分析 Nginx 访问日志,将高频正常 IP(如办公网、合作方 IP 段)加入 Lua 脚本白名单,减少误封概率,可通过awk命令统计 Top10 正常 IP:

awk '{print $1}' /var/log/nginx/access.log | grep -v "192.168.1.100" | sort | uniq -c | sort -nr | head -10

八、总结与最佳实践

Nginx 动态封禁 IP 的核心是 “按需选择方案,兼顾安全与性能”,总结 3 条最佳实践:

  1. 最小权限原则:封禁范围越小越好,优先站点级封禁(而非全局封禁),避免影响其他业务;
  1. 性能优先:静态资源无需 Lua 校验,共享内存大小根据并发 IP 数合理配置,避免内存浪费;
  1. 定期复盘:每周分析封禁日志,优化阈值与白名单,对长期无攻击的 IP 及时解封,保持防护策略动态适配业务变化。

通过本文的 3 种核心方案与扩展技巧,可构建从 “临时应急” 到 “智能集群防护” 的完整 IP 封禁体系,有效抵御大部分恶意 IP 攻击,保障服务器稳定运行。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐