Hardening de Nginx en HestiaCP: cómo mitigar los ataques DDoS de Capa 7 y optimizar la caché de contenido dinámico
Cómo personalizar las plantillas Nginx de HestiaCP para limitar peticiones, bloquear conexiones lentas y activar la FastCGI Cache, reduciendo el impacto de los ataques DDoS de aplicación y acelerando las páginas dinámicas.
Quien administra un servidor con HestiaCP tarde o temprano se encuentra con un problema conocido: el sitio responde lentamente, la CPU se dispara y los registros de Nginx se llenan de miles de peticiones que, por separado, parecen inofensivas. A menudo no se trata de un pico de tráfico "bueno", sino de un ataque DDoS de aplicación: un tipo de ataque que no busca saturar el ancho de banda de la red, sino agotar los recursos del servidor (CPU, memoria, conexiones a la base de datos) enviando peticiones HTTP que parecen perfectamente legítimas. En esta guía vamos a intervenir directamente sobre las plantillas de Nginx de HestiaCP para añadir protecciones contra este tipo de ataque, y de paso aprovechar el sistema de caché de Nginx para aliviar la carga sobre las páginas dinámicas (por ejemplo, generadas por PHP), haciendo el sitio más resistente incluso cuando el tráfico es legítimo pero muy elevado.
Qué es un ataque DDoS de "Capa 7" y por qué involucra a Nginx
Los ataques DDoS (Distributed Denial of Service, denegación de servicio distribuida) se dividen en varias categorías según la capa del modelo OSI a la que apuntan. Los ataques a las capas 3 y 4 (red y transporte) buscan saturar el ancho de banda o las tablas de conexión, y suelen filtrarse antes de llegar al servidor, por ejemplo por el proveedor de hosting o un servicio de protección como una CDN. Los ataques de Capa 7 (la capa de "aplicación", donde vive el protocolo HTTP) son más insidiosos porque cada petición individual es casi indistinguible de la de un usuario real: una petición a una página de login, a una búsqueda en el sitio o a un endpoint que consulta la base de datos, repetida miles de veces por segundo desde muchas direcciones IP distintas, puede poner de rodillas a un servidor incluso con una conexión de red lejos de estar saturada.
Como Nginx es el servidor web que recibe primero cada petición HTTP en una instalación de HestiaCP (tanto si trabaja solo como si actúa de proxy inverso delante de Apache), es el lugar ideal para aplicar las primeras contramedidas: puede limitar cuántas peticiones acepta desde una misma dirección IP, cuántas conexiones simultáneas mantiene abiertas, y puede "absorber" parte del tráfico sirviendo respuestas desde la caché en lugar de regenerarlas cada vez.
Por qué no se deben modificar las plantillas originales de HestiaCP
En HestiaCP, la configuración de Nginx de cada dominio no se escribe a mano: se genera automáticamente a partir de archivos modelo llamados plantillas, que contienen marcadores como %domain%, %user%, %docroot% o %backend_lsnr%, sustituidos por los valores reales del sitio cuando la configuración se crea o se reconstruye. Las plantillas se encuentran en dos ubicaciones del servidor, según el escenario:
- Nginx como único servidor web (con PHP-FPM):
/usr/local/hestia/data/templates/web/nginx/php-fpm/ - Nginx como proxy inverso delante de Apache:
/usr/local/hestia/data/templates/web/nginx/
La documentación oficial de HestiaCP recomienda expresamente no modificar los archivos de plantilla predeterminados (por ejemplo default.tpl): cada vez que se actualiza el panel, estos archivos se sobrescriben y los cambios se perderían. El procedimiento correcto es copiar la plantilla que se quiere personalizar con un nuevo nombre, y modificar solo las copias. Cada plantilla suele necesitar varios archivos con el mismo nombre base pero extensiones distintas, por ejemplo hardened.tpl, hardened.stpl (la variante con certificado SSL) y, si existe, el script de soporte hardened.sh.
Crear la plantilla personalizada paso a paso
Tras conectarse al servidor por SSH como root (o con un usuario con los permisos adecuados), crear una plantilla basada en la predeterminada se hace así:
cd /usr/local/hestia/data/templates/web/nginx/php-fpm/
cp default.tpl hardened.tpl
cp default.stpl hardened.stpl
A partir de aquí, hardened.tpl y hardened.stpl son dos copias independientes que se pueden modificar sin riesgo de perder los cambios en la próxima actualización de HestiaCP. Las secciones siguientes muestran qué añadir dentro de estos archivos para obtener las dos protecciones principales: limitación de peticiones y caché.
Limitar las peticiones por dirección IP: limit_req
La primera y más eficaz contramedida contra un ataque de aplicación es impedir que una sola dirección IP envíe un número excesivo de peticiones en poco tiempo. Nginx ofrece para ello el módulo limit_req, que funciona en dos pasos: primero se define una "zona de memoria compartida" que registra las peticiones por IP, y luego se aplica esa zona a uno o más bloques location de la configuración.
La definición de la zona va en el bloque http global de Nginx (archivo /etc/nginx/nginx.conf, o un archivo dentro de /etc/nginx/conf.d/ incluido automáticamente), no en la plantilla de cada dominio:
# /etc/nginx/conf.d/ratelimit.conf
limit_req_zone $binary_remote_addr zone=generic:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
Aquí generic es una zona que permite una media de 30 peticiones por minuto por IP en todo el sitio, mientras que login es una zona más estricta (10 peticiones por minuto) pensada específicamente para las páginas de autenticación, el blanco favorito habitual de este tipo de ataque. El valor 10m indica cuánta memoria se reserva para registrar direcciones IP: 10 MB bastan para unas 160.000 direcciones distintas, más que suficiente para la mayoría de los sitios.
Hecho esto, dentro de la plantilla hardened.tpl se aplica la zona genérica a todo el sitio y la más estricta a la página de login:
server {
# ... directivas generadas automáticamente por HestiaCP ...
limit_req zone=generic burst=15 nodelay;
location = /wp-login.php {
limit_req zone=login burst=5 nodelay;
include /etc/nginx/fastcgi_params;
fastcgi_pass %backend_lsnr%;
}
}
El parámetro burst permite absorber breves ráfagas de peticiones legítimas (por ejemplo cuando un navegador carga varios recursos en paralelo) sin bloquear al visitante, mientras que nodelay evita que las peticiones en exceso se pongan en cola, devolviendo de inmediato un error 503 Service Temporarily Unavailable a quien supere el límite. Así, un bot que machaca la página de login empieza enseguida a recibir errores y deja de consumir recursos del servidor, mientras que un visitante normal no nota nada.
Limitar las conexiones simultáneas: limit_conn
Otra técnica complementaria consiste en limitar cuántas conexiones abiertas al mismo tiempo puede tener una sola IP, útil contra ataques que abren muchas conexiones y las mantienen ocupadas el mayor tiempo posible. Aquí también la lógica es de dos pasos:
# /etc/nginx/conf.d/ratelimit.conf
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
limit_conn perip 20;
}
Con esta configuración, una misma dirección IP no podrá tener más de 20 conexiones abiertas simultáneamente hacia ese sitio: por encima de ese umbral, Nginx responde con un error sin ni siquiera pasar la petición al backend PHP o a Apache, ahorrando recursos valiosos.
Protegerse de las conexiones lentas (Slowloris)
Una familia de ataques de aplicación, conocida como Slowloris, no envía muchas peticiones, sino que abre conexiones y las mantiene ocupadas enviando datos gota a gota, muy lentamente, para agotar el número máximo de conexiones que el servidor puede gestionar. La contramedida es establecer tiempos de espera estrictos para la recepción de cabeceras y cuerpo de la petición, de forma que Nginx cierre las conexiones demasiado lentas:
server {
client_header_timeout 10s;
client_body_timeout 10s;
send_timeout 10s;
}
Los valores predeterminados de Nginx para estos tiempos de espera son de 60 segundos, tiempo más que suficiente para que un ataque Slowloris sature las conexiones disponibles; reducirlos a 10 segundos (un valor que se puede ajustar según la calidad media de las conexiones de los visitantes reales del sitio) reduce drásticamente la ventana de ataque sin impacto perceptible para usuarios con una conexión normal.
Bloquear IPs y User-Agents sospechosos
Cuando, al analizar los registros durante un ataque, se identifican direcciones IP recurrentes o un User-Agent anómalo usado por el tráfico malicioso (muchos bots simples usan un User-Agent vacío o claramente falso), se puede añadir un bloqueo directo en la plantilla:
server {
# Bloquear un rango de IPs
deny 203.0.113.0/24;
# Bloquear peticiones con User-Agent vacío o sospechoso
if ($http_user_agent ~* "^$|curl|wget|python-requests") {
return 403;
}
}
Esta técnica debe usarse con cautela: bloquear por User-Agent solo es útil contra los bots más rudimentarios, porque los más sofisticados imitan perfectamente el User-Agent de un navegador real, pero sigue siendo un filtro de coste casi nulo que puede eliminar parte del tráfico no deseado antes de que llegue al resto de la configuración.
Optimizar la caché de contenido dinámico con FastCGI Cache
Las protecciones vistas hasta ahora reducen el tráfico malicioso, pero el segundo pilar de la resiliencia consiste en lograr que el tráfico legítimo (aunque sea muy elevado, por ejemplo durante un pico de visitas orgánico) pese lo menos posible sobre el servidor. Para los sitios basados en PHP (WordPress incluido) gestionados por HestiaCP con Nginx y PHP-FPM, la solución es la FastCGI Cache integrada en Nginx: la primera vez que se solicita una página, Nginx la genera normalmente pasando la petición a PHP-FPM, pero guarda una copia; las peticiones siguientes a la misma página se sirven directamente desde la caché, sin volver a involucrar a PHP ni a la base de datos, con una enorme ganancia de velocidad y una carga casi nula sobre el servidor.
También aquí la zona de caché debe definirse a nivel global:
# /etc/nginx/conf.d/fastcgi-cache.conf
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WPCACHE:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
Después, dentro de la plantilla hardened.tpl, HestiaCP prevé un mecanismo específico: basta añadir, justo después del include fastcgi_params en el bloque que gestiona los archivos PHP, una referencia al archivo de configuración por dominio que el panel genera automáticamente cuando la caché se activa desde la interfaz:
location ~ [^/]\.php(/|$) {
fastcgi_pass %backend_lsnr%;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;
fastcgi_cache WPCACHE;
fastcgi_cache_valid 200 60m;
fastcgi_cache_use_stale updating error timeout invalid_header http_500;
add_header X-FastCGI-Cache $upstream_cache_status;
}
La cabecera X-FastCGI-Cache es muy útil para verificar el funcionamiento: aparece en la respuesta HTTP con el valor HIT cuando la página se sirve desde la caché, MISS cuando se genera desde PHP y se guarda en caché por primera vez, y se puede comprobar fácilmente desde el navegador (herramientas de desarrollador, pestaña Red) o desde la terminal con curl -I https://tusitio.com/. La opción fastcgi_cache_use_stale con el parámetro updating es especialmente importante durante un pico de tráfico: si la caché de una página caduca justo en el momento en que llegan cientos de peticiones simultáneas, Nginx envía solo una de ellas a PHP para regenerarla y sigue sirviendo a todas las demás la versión anterior (ligeramente caducada) hasta que la nueva esté lista, evitando que todas las peticiones golpeen el backend a la vez.
No todas las páginas deben almacenarse en caché: el área de administración de un CMS, o las páginas que muestran contenido personalizado para el usuario conectado, deben excluirse, de lo contrario un usuario podría ver los datos de otro. HestiaCP ya gestiona en parte estos casos a través del archivo nginx.fastcgi_cache.conf generado por el panel, pero conviene comprobar, para CMS específicos como WordPress, que cookies como las de sesión o de carrito de compra desactiven correctamente la caché para esa petición mediante una directiva fastcgi_cache_bypass basada en el valor de la cookie.
Aplicar la nueva plantilla y verificar el resultado
Una vez listos los archivos hardened.tpl y hardened.stpl, activarlos en un dominio se gestiona enteramente desde la interfaz de HestiaCP:
- Acceder al panel, abrir Web > [nombre del dominio] > Edit.
- En el menú desplegable de plantilla Nginx, seleccionar la nueva plantilla
hardened(aparecerá automáticamente en la lista una vez copiados los archivos). - Guardar: HestiaCP regenera la configuración del dominio a partir de la nueva plantilla.
Alternativamente, desde la línea de comandos, el mismo resultado se obtiene con:
v-change-web-domain-tpl usuario dominio.es hardened
v-rebuild-web-domain usuario dominio.es yes
donde el último parámetro yes fuerza también el reinicio de los servicios web implicados. Antes de aplicar el cambio en producción, siempre es recomendable verificar la sintaxis de la configuración con nginx -t, que señala posibles errores sin interrumpir el servicio en marcha, y probar el comportamiento de la caché y de los límites de peticiones en un dominio de prueba o en un horario de bajo tráfico.
Para vaciar la caché FastCGI tras una modificación del sitio (por ejemplo una actualización de tema o de contenidos), HestiaCP ofrece el botón "Purge Nginx Cache" en la página de edición del dominio, o el comando v-purge-nginx-cache usuario dominio.es desde la línea de comandos.
Conclusiones
Ninguna de las medidas descritas en esta guía es, por sí sola, una protección completa contra un ataque DDoS de aplicación serio y prolongado: para eso se necesitan soluciones dedicadas por delante del servidor, como una CDN con protección anti-DDoS o un cortafuegos de aplicaciones web (WAF). Sin embargo, combinar la limitación de peticiones, la limitación de conexiones, tiempos de espera estrictos para clientes lentos y una caché FastCGI bien configurada en las plantillas Nginx de HestiaCP eleva de forma significativa el umbral de tráfico malicioso que el servidor puede absorber por sí solo, y al mismo tiempo mejora el rendimiento del sitio en los momentos de tráfico legítimo elevado. Es el tipo de medida de "bajo coste, alto beneficio" que vale la pena aplicar en cualquier servidor HestiaCP expuesto públicamente en internet.
Fuentes oficiales
- Documentación de HestiaCP – Web Templates (FastCGI/Proxy Cache)
- Foro de HestiaCP – Nginx custom configuration per site
- Blog F5 NGINX – Mitigating DDoS Attacks with NGINX and NGINX Plus
- Documentación oficial de Nginx – ngx_http_limit_req_module
- Documentación oficial de Nginx – ngx_http_fastcgi_module
Compartir
¿Cuál es tu reacción?
Me gusta
0
No me gusta
0
Me encanta
0
Divertido
0
Enfadado
0
Triste
0
¡Vaya!
0