Si los logs de un sitio empiezan a mostrar una gran cantidad de respuestas 404 y 400, muchas veces no se debe a usuarios haciendo clic en enlaces rotos. Lo habitual es que un escáner automático esté probando rutas como .env, .git, wp-admin, phpmyadmin o xmlrpc.php.
Estas solicitudes generan varios problemas:
- el access log crece rápido;
- el error log se llena de ruido inútil;
- sitios estáticos o servicios de proxy inverso desperdician conexiones en solicitudes inválidas;
- los problemas reales quedan enterrados bajo ruido de escaneo.
Nginx puede controlar esto con limit_req y limit_conn. Pero hay un punto importante: Nginx no puede limitar directamente por “el código de respuesta será 404 o 400”, porque el rate limit se aplica antes de generar la respuesta.
La práctica correcta es limitar por adelantado las rutas de escaneo, orígenes sospechosos y solicitudes globales de alta frecuencia que normalmente terminan produciendo 404 / 400.
Idea básica
Conviene dividirlo en tres capas:
- Aplicar un límite suave para todo el sitio y evitar que una sola IP golpee demasiado.
- Aplicar un límite estricto a rutas comunes de escaneo y devolver
404directamente. - Limitar conexiones concurrentes por IP.
Una forma segura de desplegarlo es empezar con reglas de rutas de escaneo y access_log off, observar un día y, si todavía hay muchas rutas aleatorias con 404, añadir limit_req global.
Definir primero las zonas en http
limit_req_zone y limit_conn_zone deben estar dentro de http {}. No pueden colocarse dentro del server {} de un sitio concreto.
Puedes añadirlas al bloque http {} de /etc/nginx/nginx.conf:
|
|
También puedes crear un archivo nuevo:
|
|
Con este contenido:
|
|
Esto presupone que tu nginx.conf incluye dentro de http {}:
|
|
Usar las zonas dentro de server
La configuración de un sitio suele estar en algo como /etc/nginx/sites-enabled/www.example.com y normalmente contiene un bloque server {}. Ahí no debes volver a escribir limit_req_zone; solo se usan las zonas ya definidas.
Ejemplo:
|
|
Si te preocupa afectar tráfico normal con el límite global, empieza solo con la regla de rutas de escaneo:
|
|
Qué significan estos parámetros
Esta línea:
|
|
significa:
limit_req_zone: define la zona de contabilidad para limitar solicitudes.$binary_remote_addr: usa la IP del cliente como clave y consume menos memoria que$remote_addr.zone=perip_general:20m: crea una zona de memoria compartida llamadaperip_generalde20m.rate=5r/s: cada IP puede hacer, en promedio, 5 solicitudes por segundo.
Esta línea:
|
|
es similar, pero más estricta:
perip_scan: zona dedicada a rutas sospechosas de escaneo.rate=1r/s: cada IP solo puede hacer 1 solicitud por segundo.
Para conexiones concurrentes:
|
|
Esto limita cada IP a un máximo de 20 conexiones simultáneas.
Cómo entender burst y nodelay
Ejemplo:
|
|
Se puede leer así:
rate=5r/s: la tasa media a largo plazo es de 5 solicitudes por segundo.burst=30: se permiten 30 solicitudes extra en una ráfaga corta.nodelay: si se supera la tasa media pero no elburst, Nginx procesa inmediatamente; solo rechaza cuando se supera elburst.
Sin nodelay, Nginx intenta retrasar y encolar parte de las solicitudes. Para páginas normales, nodelay suele ser más fácil de razonar. Para APIs sensibles, conviene ajustar según el comportamiento real.
Error común: limit_req_zone en el lugar equivocado
Si ves un error como:
|
|
significa que limit_req_zone está en un contexto no permitido.
Un error típico es ponerlo dentro de server {}:
|
|
Esto no funciona.
Regla sencilla:
limit_req_zonedefine la zona, va enhttp {}.limit_requsa la zona, va enserver {}olocation {}.limit_conn_zonedefine la zona de conexiones, va enhttp {}.limit_connusa la zona de conexiones, va enserver {}olocation {}.
Bloquear temporalmente IPs claramente anómalas
Si los logs confirman que algunas IPs envían escaneos de forma continua, puedes bloquearlas temporalmente:
|
|
Estas directivas deny pueden ir en server {} o en un location {} concreto. Mantenerlas a largo plazo depende del riesgo de falsos positivos y del origen del tráfico.
Comprobar y recargar
Primero comprueba la configuración:
|
|
Si no hay errores, recarga:
|
|
No reinicies directamente el servicio. reload permite que Nginx cargue la nueva configuración de forma suave y con menos riesgo.
Parámetros recomendados
Para un sitio personal o estático, puedes empezar con:
- páginas normales:
rate=5r/sa10r/s; - rutas de escaneo:
rate=1r/s; - rutas de escaneo:
burst=5; - límite global:
burst=30; - concurrencia por IP:
10a20.
Si el tráfico normal es muy bajo, puedes ser más estricto. Si el sitio tiene muchas imágenes, scripts o API, relaja el límite de páginas normales para no afectar visitas reales.
La forma más estable es desplegar por fases:
- Aplicar primero
access_log off+return 404a rutas de escaneo. - Añadir después el límite estricto
perip_scan. - Observar logs durante un día.
- Si siguen apareciendo muchos 404 aleatorios, activar el límite global suave.