How to Dynamically Block IPs with Nginx, Lua, and Redis
This guide explains how to use Nginx, Lua (via OpenResty), and Redis to automatically block any IP that exceeds 60 requests per minute, storing the blacklist in Redis with a default one‑day ban and providing performance optimizations and testing steps.
Background
Frequent brute‑force requests and malicious crawlers can overload databases and services. The requirement is to block any IP that makes more than 60 requests within one minute, adding it to Redis and denying traffic for a default period of one day.
Technical Choice
The solution uses Nginx with the Lua module (via OpenResty) and Redis. A Lua script runs on each request, checks a Redis blacklist, counts accesses, and bans the IP when the threshold is exceeded.
Environment Setup
Install OpenResty – provides Nginx with built‑in Lua support.
# Ubuntu/Debian
sudo apt-get install openresty
# CentOS
yum install openrestyInstall Redis
sudo apt-get install redis-server # Debian
sudo yum install redis # RedHatNginx Configuration
nginx.conf – add a shared memory dictionary and point to the Lua script.
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict ip_limit 10m;
server {
listen 80;
server_name _;
location / {
access_by_lua_file /usr/local/lua/ip_block.lua;
root /var/www/html;
}
}
}Lua Script (ip_block.lua)
File location – /usr/local/lua/ip_block.lua
Script content
local redis = require "resty.redis"
local red = redis:new()
-- Redis connection parameters
local redis_host = "127.0.0.1"
local redis_port = 6379
local redis_timeout = 1000 -- ms
local redis_auth = nil -- no password
-- Blocking parameters
local block_time = 86400 -- 1 day
local time_window = 60 -- 1 minute
local max_requests = 60
local function get_client_ip()
local headers = ngx.req.get_headers()
return headers["X-Real-IP"]
or headers["x_forwarded_for"]
or ngx.var.remote_addr
end
local function connect_redis()
red:set_timeout(redis_timeout)
local ok, err = red:connect(redis_host, redis_port)
if not ok then
ngx.log(ngx.ERR, "Redis connection failed: ", err)
return nil
end
if redis_auth then
ok, err = red:auth(redis_auth)
if not ok then
ngx.log(ngx.ERR, "Redis auth failed: ", err)
end
end
return ok
end
local client_ip = get_client_ip()
local counter_key = "limit:count:" .. client_ip
local block_key = "limit:block:" .. client_ip
-- Check existing block
local is_blocked = red:get(block_key)
if tonumber(is_blocked) == 1 then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
connect_redis()
local current_count = red:incr(counter_key)
if current_count == 1 then
red:expire(counter_key, time_window)
end
if current_count > max_requests then
red:setex(block_key, block_time, 1)
red:del(counter_key)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
red:set_keepalive(10000, 100)Performance Optimizations
Redis connection pool – reuse connections with set_keepalive.
Shared memory cache – store hot IPs in lua_shared_dict to reduce Redis lookups.
Asynchronous logging – write block events with a non‑blocking timer.
ngx.timer.at(0, function()
local log_msg = string.format("%s - IP %s blocked at %s",
ngx.var.host, client_ip, ngx.localtime())
local log_file = io.open("/var/log/nginx/blocked_ips.log", "a")
log_file:write(log_msg, "
")
log_file:close()
end)Verification & Testing
Manual block test – use ab -n 100 -c 10 http://your-domain.com/ then check Redis keys with redis-cli keys "limit:block:*".
Automatic un‑block – after 24 hours verify the key expires with redis-cli ttl "limit:block:1.2.3.4".
Extended Ideas
Distributed blocking – share the Redis blacklist across multiple Nginx nodes for cluster‑wide protection.
Visualization – use Grafana + Prometheus with prometheus-redis-exporter to monitor real‑time block statistics.
Dynamic thresholds – store per‑path limits in a Redis hash and read them in the Lua script.
local rule_key = "limit:rule:" .. ngx.var.uri
local custom_rule = red:hget(rule_key, "max_requests")Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
