SoFunction
Updated on 2025-04-09

Nginx implements a method of dynamically intercepting illegal access to IP

Background: Access will be brutally swiped, crawlers and malicious attacks from time to time, causing databases, services, etc. to be paralyzed

Requirements: Implement a method of dynamically intercepting IP on Nginx. Specifically, when an IP accesses more than 60 times in 1 minute, it is added to Redis and intercepted. The interception time is 1 day by default.

Technical selection: The method of using Nginx+Lua+Redis. This solution uses Lua script to check the blacklist in Redis when Nginx handles requests, and at the same time counts the access frequency, and blocks it if it exceeds the threshold. This should meet the needs of the user.

It is necessary to combine Lua scripts and Redis counting functions. Install OpenResty, configure Nginx's Lua module, write Lua scripts to count visits, use Redis storage and expiration keys, and set interception logic. Use of connection pools to avoid frequent connections to Redis to affect performance.

1. Environmental preparation

  • Install OpenResty

OpenResty integrates Nginx and Lua modules, and supports running Lua scripts directly:

# Ubuntu/Debian
sudo apt-get install openresty
# CentOS
yum install openresty
  • Install Redis Service
sudo apt-get install redis-server  #Debiansudo yum install redis             # RedHat

2. Nginx configuration

  • Main configuration file ()existhttpAdd shared memory and Lua script paths to the block:

http {
    lua_package_path "/usr/local/openresty/lualib/?.lua;;";
    lua_shared_dict ip_limit 10m;  # Shared memory area
    server {
        listen 80;
        server_name _;

        location / {
            access_by_lua_file /usr/local/lua/ip_block.lua;  # Core intercept script            root /var/www/html;
        }
    }
}

3. Lua script implements dynamic interception

  • Script pathCreate a Lua script:/usr/local/lua/ip_block.lua

  • Script content

local redis = require ""
local red = redis:new()

-- RedisConnection parameters
local redis_host = "127.0.0.1"
local redis_port = 6379
local redis_timeout = 1000  -- millisecond
local redis_auth = nil       -- No password left blank

-- Intercept parameters
local block_time = 86400     -- Banned time(1sky)
local time_window = 60       -- Statistics window(1minute)
local max_requests = 60     -- Maximum number of requests

-- Get the clientIP
local function get_client_ip()
    local headers = .get_headers()
    return headers["X-Real-IP"] or headers["x_forwarded_for"] or .remote_addr
end

-- connectRedis
local function connect_redis()
    red:set_timeout(redis_timeout)
    local ok, err = red:connect(redis_host, redis_port)
    if not ok then
        (, "Redis connection failed: ", err)
        return nil
    end
    if redis_auth then
        local ok, err = red:auth(redis_auth)
        if not ok then (, "Redis authentication failed: ", err) end
    end
    return ok
end

-- Main logic
local client_ip = get_client_ip()
local counter_key = "limit:count:" .. client_ip
local block_key = "limit:block:" .. client_ip

-- Check if it has been blocked
local is_blocked, err = red:get(block_key)
if tonumber(is_blocked) == 1 then
    (ngx.HTTP_FORBIDDEN)  -- Return directly403
end

-- Statistics the number of requests
connect_redis()
local current_count = red:incr(counter_key)
if current_count == 1 then
    red:expire(counter_key, time_window)  -- Set expiration time for the first time
end

-- Trigger the ban condition
if current_count > max_requests then
    red:setex(block_key, block_time, 1)   -- Block and set1sky过期
    red:del(counter_key)                  -- Delete the counter
    (ngx.HTTP_FORBIDDEN)
end

-- releaseRedisconnect
red:set_keepalive(10000, 100)

4. Performance optimization

  • Redis connection poolpassset_keepaliveMultiplex connections to avoid frequent TCP connection establishment
  • Shared memory cacheuselua_shared_dictCache high-frequency access IP to reduce Redis query pressure
  • Asynchronous loggingBlock the operation asynchronously writes to log files to avoid blocking request processing:

(0, function()
    local log_msg = ("%s - IP %s blocked at %s", 
        , client_ip, ())
    local log_file = ("/var/log/nginx/blocked_ips.log", "a")
    log_file:write(log_msg, "\n")
    log_file:close()
end)

V. Verification and testing

  • Manually trigger ban
# Simulate high frequency requestsab -n 100 -c 10 /
# Check Redisredis-cli keys "limit:block:*"
  • Automatic unblocking verification

After waiting for 24 hours, check whether the blocked IP is automatically deleted:

redis-cli ttl "limit:block:1.2.3.4"  # Return the remaining seconds

VI. Expansion plan

  • Distributed ban
    Sharing Redis blacklists among multiple Nginx servers to achieve cluster-level interception
  • Visual monitoring
    Real-time intercept data is displayed through Grafana+Prometheus:

# Collect Redis indicatorsprometheus-redis-exporter --=localhost:6379
  • Dynamically adjust threshold
    Storing interception rules for different paths through Redis Hash:
local rule_key = "limit:rule:" .. 
local custom_rule = red:hget(rule_key, "max_requests")

Quote instructions

  • The core intercept logic refers to the classic architecture design of Nginx+Lua+Redis
  • Redis key expiration mechanism ensures automatic unblocking
  • Performance optimization solutions draw on OpenResty best practices

The above is the detailed content of Nginx's method to dynamically intercept illegal access to IP. For more information about Nginx's dynamic intercepting IP, please pay attention to my other related articles!