Imagina: 02:17, pico nocturno. Un upstream está en las últimas, la cola está creciendo, los analistas piden que se incluya una nueva función solo para una parte del tráfico y el marketing pide que no se pierda la velocidad de las páginas. La solución no está en el backend. La solución está en el «borde»: Nginx con Lua, más un «bloc de notas de estado» rápido: Redis. En milisegundos, activas el indicador de función, rediriges las solicitudes, limitas a los clientes «parladores» y devuelves la caché donde sea seguro. ⚡
Lua en Nginx es un mini-cerebro justo en la puerta de entrada de tu servicio: ve encabezados, cookies, patrones extraños, habla con Redis y toma decisiones pequeñas pero críticas antes de que la solicitud llegue a la aplicación. Redis proporciona atomicidad y velocidad: contadores, cuotas, banderas, idempotencia, sin carreras ni transacciones pesadas.

🧠 ¿Por qué Lua está al límite?
⚡ Velocidad y E/S no bloqueantes: OpenResty/ngx_lua funciona en un modelo de eventos, sin bloqueos.
🧩 Integrabilidad: los scripts gobiernan el comportamiento directamente en las fases de Nginx (rewrite/access/header/body/log).
🔒 Atomicidad en Redis: los scripts de Lua se ejecutan como una sola operación (sin carreras de datos).
Idea clave: «un poco de lógica en la entrada» ahorra recursos de backend y proporciona un control preciso.
🌐 Usos inesperados en Nginx + Lua
🎛️ Edge-feature flags: activamos las funciones por segmentos (país, versión del cliente) antes de entrar en la aplicación.
🧭 Enrutamiento inteligente: dirigimos el tráfico a planes de suscripción/variantes AB, leemos las reglas de Redis.
🛡️ Micro-WAF: bloqueos «suaves» basados en firmas de comportamiento, sin interferir con los usuarios legales.
🧯 Disyuntor en la entrada: cortamos temporalmente el upstream enfermo, damos caché/«stub».
🧹 Normalización de URL: canonicización, compresión de query basura, lucha contra duplicados para caché.
🔗 Cómo se conecta con Redis
🏷️ Banderas/reglas viven en Redis con TTL → distribución instantánea sin reiniciar Nginx.
🧪 A/B: la opción del usuario se almacena en Redis → consistencia entre consultas.
🚦 Rate limiting: cubos de tokens/embudos para llaves
IP+endpointouser_id.🔁 Request coalescing: una solicitud al backend, el resto está en espera (silenciamos el «efecto de manada»).
🧩 Ejemplo: validación de JWT y límite dinámico
1) Validamos el token directamente en access_by_lua
access_by_lua_block {
local jwt = require "resty.jwt"
local validators = require "resty.jwt-validators"
local auth = ngx.var.http_authorization or ""
local token = auth:match("Bearer%s+(.+)")
if not token then return ngx.exit(ngx.HTTP_UNAUTHORIZED) end
local ok = jwt:verify("shared-secret", token, {
validators.set_system_leeway(60),
validators.require_aud("my-api"),
})
if not ok.valid then return ngx.exit(ngx.HTTP_FORBIDDEN) end
ngx.ctx.user_id = ok.payload.sub
}2) Token bucket en Redis a través de Lua (atómico)
-- KEYS[1]=ключ ведра; ARGV: now(ms), rate(ток/сек), burst
local key = KEYS[1]
local now = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local burst= tonumber(ARGV[3])
local data = redis.call("HMGET", key, "tokens", "ts")
local tokens = tonumber(data[1]) or burst
local ts = tonumber(data[2]) or now
local delta = math.max(0, now - ts) / 1000 * rate
tokens = math.min(burst, tokens + delta)
local allowed = tokens >= 1
if allowed then tokens = tokens - 1 end
redis.call("HMSET", key, "tokens", tokens, "ts", now)
redis.call("PEXPIRE", key, math.floor((burst/rate)*1000))
return allowed and 1 or 03) Llamada desde Nginx-Lua
local redis = require "resty.redis"
local r = redis:new()
r:set_timeout(50)
assert(r:connect("redis", 6379))
local key = "tb:" .. (ngx.ctx.user_id or ngx.var.remote_addr) .. ":" .. ngx.var.uri
local allowed = r:eval(token_bucket_script, 1, key, ngx.now()*1000, 5, 10)
r:set_keepalive(1_000, 100) -- пул коннекшенов
if allowed == 0 then return ngx.exit(429) end💡 La lógica del límite se puede mezclar: los VIP obtienen un burst más grande de Redis.
🧰 Más trucos con Redis+Lua
🔐 Idempotencia de la API: clave de tipo
idem:<hash>con TTL — cortamos las repeticiones.📦 Asignación única de tareas: movemos atómicamente los elementos del conjunto a la lista «en progreso».
🔒 Rizos ligeros:
SET key val NX PX ttl; para distribuidos, con cuidado con Redlock (evalúa los riesgos de la red/horas).🧮 Cuotas/límites: contadores por usuario/organización, restablecimiento programado.

📏 Cuándo es mejor el «borde» y cuándo no
Guion | Lua en Nginx/Redis | Mejor en la aplicación |
|---|---|---|
Enrutamiento, características, límites | ✅ Solución instantánea en la entrada | — |
Cálculos pesados, ML, render | ❌ No es lugar para un CPU devorador | ✅ En el backend/trabajador |
Operaciones de datos atómicos | ✅ Redis Lua — perfecto | — |
Reglas de negocio y transformaciones complejas | ⚠️ Se puede, pero con cuidado | ✅ Legibilidad en la base de código |
En Codice hacemos que el aprendizaje de la programación sea emocionante y comprensible: tenemos cursos interesantes con tareas que ayudan a mejorar las habilidades paso a paso.
Y también tenemos un canal de telegram, donde discutimos ideas geniales, compartimos experiencias y analizamos juntos las tareas: aprender no solo es útil, sino también divertido.
🧵 Resultados
Lua en el «borde» ofrece soluciones instantáneas: banderas, límites, enrutamiento, coalescing.
Los scripts de Redis proporcionan atomicidad y un alto rendimiento.
Lo principal es no transferir la lógica empresarial pesada a Nginx; úsalo como un filtro inteligente.
¿Qué tarea pondrías en el «borde» a continuación? Escríbelo, lo analizaremos en la continuación. 💬
