首页 > 资源分享 > 轻量应用服务器+宝塔+Nginx实现轻量级防爬虫(自动封禁IP)

轻量应用服务器+宝塔+Nginx实现轻量级防爬虫(自动封禁IP)

时间:2026-07-20浏览次数:6

大致思路:

  1. 读取所有网站最近两分钟的日志。

  2. 合并统计所有站点的 IP。

  3. 超过200次的 IP 写入公共封禁文件。

  4. 自动检测并重载 Nginx。

  5. 已封禁的 IP 不会重复写入。

这个简化版只处理 IPv4,封禁后不会自动解封,需要手动解除。

第一步:创建公共封禁文件

执行:

touch /www/server/nginx/conf/blockips.conf
chmod 644 /www/server/nginx/conf/blockips.conf

编辑 Nginx 主配置:

/www/server/nginx/conf/nginx.conf

http {} 中,找到:

include /www/server/panel/vhost/nginx/*.conf;

在它前面添加:

# 所有网站共用的IP封禁名单
include /www/server/nginx/conf/blockips.conf;

最终类似:

http {
    # 原来的配置……

    include /www/server/nginx/conf/blockips.conf;
    include /www/server/panel/vhost/nginx/*.conf;
}

检查:

/www/server/nginx/sbin/nginx -t

成功后重载:

/www/server/nginx/sbin/nginx -s reload

作用:以后只要公共文件出现

deny 1.2.3.4;

这个 IP 访问任何网站都会立即收到403,不会进入 PHP。


第二步:在宝塔创建计划任务

进入宝塔:计划任务 → 添加任务

填写:

  • 任务类型:Shell脚本

  • 任务名称:Nginx自动封禁高频IP

  • 执行周期:N分钟

  • 分钟:1

  • 脚本内容:复制下面完整脚本

#!/bin/bash

# 防止上一次任务未结束时重复运行
exec 9>/tmp/nginx-auto-ban.lock
flock -n 9 || exit 0

# ==================================================
# 可以修改的设置
# ==================================================

# 两分钟内访问达到多少次就封禁
THRESHOLD=50

# 如果你有固定管理IP,可以填写
# 没有固定IP就保持为空
ADMIN_IP=""

# 公共封禁文件
BAN_FILE="/www/server/nginx/conf/blockips.conf"

# Nginx程序
NGINX="/www/server/nginx/sbin/nginx"

# ==================================================
# 以下内容通常不需要修改
# ==================================================

# 当前分钟和上一分钟
NOW=$(LC_ALL=C date '+%d/%b/%Y:%H:%M')
PREV=$(LC_ALL=C date -d '1 minute ago' '+%d/%b/%Y:%H:%M')

# 临时文件
OLD_FILE="/tmp/nginx-blockips-old.$$"
NEW_FILE="/tmp/nginx-blockips-new.$$"
IP_FILE="/tmp/nginx-high-frequency-ips.$$"

# 任务结束后删除临时文件
trap 'rm -f "$OLD_FILE" "$NEW_FILE" "$IP_FILE"' EXIT

# 确保公共封禁文件存在
touch "$BAN_FILE"

# 备份修改前的封禁文件
cp -a "$BAN_FILE" "$OLD_FILE"
cp -a "$BAN_FILE" "$NEW_FILE"

# ==================================================
# 读取所有网站最近两分钟的访问日志
# ==================================================

for FILE in /www/wwwlogs/*.log
do
    [ -f "$FILE" ] || continue

    # 排除错误日志和系统日志
    case "$FILE" in
        *.error.log|*/nginx_error.log|*/tcp-access.log|*/tcp-error.log)
            continue
            ;;
    esac

    # 每个网站只读取最后10000行
    # 只统计当前分钟和上一分钟
    tail -n 5000 "$FILE" 2>/dev/null |
    awk -v now="$NOW" -v prev="$PREV" '
		{
			# 只统计当前分钟和上一分钟
			if (index($0, "[" now) == 0 &&
				index($0, "[" prev) == 0) {
				next
			}

			# Nginx标准日志中的第7列是请求地址
			uri = tolower($7)

			# 删除问号后面的查询参数
			# 例如:app.js?v=123 变成 app.js
			sub(/\?.*/, "", uri)

			# 资源文件不参与IP访问频率统计
			if (uri ~ /\.(js|css|map|jpg|jpeg|png|gif|webp|bmp|svg|ico|swf|woff|woff2|ttf|eot|otf|mp3|wav|ogg|mp4|webm|avi|mov)$/) {
				next
			}

			# 只输出有效IPv4
			if ($1 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) {
				print $1
			}
		}
	'
done |
sort |
uniq -c |
awk -v threshold="$THRESHOLD" '
    $1 >= threshold {
        print $2
    }
' > "$IP_FILE"

CHANGED=0

# ==================================================
# 把达到条件的IP加入公共封禁文件
# ==================================================

while read -r IP
do
    [ -n "$IP" ] || continue

    # 不封禁服务器本地地址
    case "$IP" in
        127.*)
            continue
            ;;
    esac

    # 不封禁管理员固定IP
    if [ -n "$ADMIN_IP" ] && [ "$IP" = "$ADMIN_IP" ]
    then
        continue
    fi

    RULE="deny $IP;"

    # 已经封禁的IP不重复添加
    if ! grep -qFx "$RULE" "$NEW_FILE"
    then
        echo "$RULE" >> "$NEW_FILE"
        echo "$(date '+%F %T') 准备封禁IP:$IP"
        CHANGED=1
    fi
done < "$IP_FILE"

# 没有新IP,不需要修改Nginx
if [ "$CHANGED" -eq 0 ]
then
    echo "$(date '+%F %T') 检查完成,没有发现新的高频IP"
    exit 0
fi

# 使用新的封禁文件
cp -a "$NEW_FILE" "$BAN_FILE"

# 检测完整Nginx配置
if "$NGINX" -t
then
    # 检测成功,平滑重载
    "$NGINX" -s reload
    echo "$(date '+%F %T') 新IP已经封禁,Nginx重载成功"
else
    # 检测失败,恢复修改前的文件
    cp -a "$OLD_FILE" "$BAN_FILE"
    echo "$(date '+%F %T') Nginx检测失败,已经恢复旧文件"
    exit 1
fi

脚本.txt