Nginx es muy bueno manteniendo conexiones abiertas de forma barata, por eso sobrevive a avalanchas que matan a un servidor de aplicación. Esa fortaleza es también una trampa: aceptará encantado mucho más trabajo concurrente del que puede manejar lo que tiene detrás, y luego pasará el fallo aguas abajo.
Limita conexiones antes que peticiones
Dos límites distintos resuelven dos problemas distintos. Un límite de conexiones acota cuántos sockets puede mantener abiertos una sola dirección, que es la defensa contra ataques lentos que ocupan conexiones sin enviar apenas datos. Un límite de tasa de peticiones acota con qué rapidez una dirección puede pedir trabajo, que es la defensa contra el volumen. Los sitios que solo configuran el segundo siguen siendo vulnerables al primero.
Los timeouts que importan
- Timeouts de lectura de cabecera y cuerpo: el control más eficaz contra ataques de envío lento. Valores por defecto medidos en minutos son una invitación.
- Timeout de keepalive: keepalives generosos mejoran el rendimiento del usuario real pero multiplican el coste de una avalancha de conexiones. Ajústalo con ambas cosas en mente.
- Timeouts de upstream: si la aplicación se atasca, nginx debería rendirse rápido en lugar de acumular workers en espera.
El búfer es una defensa
Cuando nginx almacena en búfer una petición completa antes de abrir una conexión upstream, un cliente lento ocupa memoria barata de nginx en vez de un costoso worker de la aplicación. Desactivar el búfer por comodidad elimina ese escudo.
Límites de tasa que no rompen a los usuarios reales
Un único límite de tasa global casi siempre es demasiado estricto para una página concurrida o demasiado laxo para servir de algo. Aplica límites distintos por clase de endpoint: los recursos estáticos pueden ser generosos, las páginas HTML moderadas y los endpoints caros — búsqueda, login, todo lo que escribe — estrictos. Permite un burst para que una carga de página normal, que dispara muchas peticiones a la vez, no sea penalizada por comportarse como un navegador.
Devuelve el estado correcto
Cuando salta un límite, responder con 429 dice a los clientes bien educados y a los buscadores que se retiren y vuelvan. Responder con un error genérico enseña a los rastreadores que la página está rota. El código de estado que eliges durante un ataque tiene consecuencias que sobreviven al ataque.
Conoce el techo
Todo esto opera sobre tráfico que ya llegó a tu máquina. El filtrado a nivel de kernel y aguas arriba existe porque, a cierto volumen, el manejo más barato posible de un paquete sigue siendo demasiado caro cuando hay millones de ellos. Ajustar nginx sube el techo; no lo elimina.