Cada día, los logs de mi servidor registran miles de escaneos automatizados de vulnerabilidades. Las peticiones tienen un aspecto bastante alarmante:
/wp-admin/wp.php/111.php/code.php/.git/config/i.php/admin/api/.env/kal.php
Lo que me sorprendió no fue la cantidad, sino lo poco que parecía importarle al servidor. Mientras observaba ese tráfico, la carga de CPU no mostraba cambios visibles.
La observación es real, pero mi explicación original era demasiado absoluta. La entrega estática no es un escudo mágico y un 404 no sale gratis. La ventaja real de mi arquitectura es más concreta: muchas sondas oportunistas cuestan muy poco porque casi no hay lógica de aplicación del lado del servidor a la que puedan llegar.
Son sondas, no una prueba de que exista una vulnerabilidad
Es fácil ver /wp-admin/wp.php o /111.php y llamar “exploit” a cada request. Técnicamente no es correcto. Lo que veo sobre todo es probing automatizado: bots prueban nombres de archivos y endpoints comunes y esperan a ver qué responde.
Algunas rutas apuntan claramente a WordPress o PHP. Otras, como /.git/config y /api/.env, buscan configuraciones o secretos publicados por error. /admin es todavía más genérico y puede ser una ruta legítima. Una petición sospechosa me dice qué está buscando el scanner, no que la vulnerabilidad exista.
En mi caso, lo importante es la arquitectura: el sitio público es HTML estático prerenderizado con Next.js y servido directamente por Nginx.
Por qué este tráfico es barato en mi stack
En las páginas de las que hablo no hay un runtime PHP ni una base de datos detrás de cada request. Nginx sirve archivos que ya existen. Si un scanner pide /111.php y ese archivo no existe, no se inicia una aplicación PHP, no se ejecuta WordPress y no se lanza ninguna consulta a base de datos. La petición simplemente falla y termina en 404.
Eso cambia el coste. En una aplicación dinámica, una request puede atravesar routing, framework, autenticación, renderizado, base de datos o llamadas a otros servicios. Mi camino estático tiene muchas menos piezas. Nginx todavía debe aceptar la conexión, procesar HTTP, localizar el recurso, escribir el log y enviar la respuesta, pero el trabajo de aplicación es pequeño.
También uso caching agresivo para el sitio, algo útil en la entrega normal de contenido estático. Aun así, no atribuiría al cache cada 404 aleatorio. Una URI basura nunca vista puede pasar por el procesamiento normal de Nginx y por una búsqueda en el sistema de archivos antes de devolver el 404.
La corrección importante: estático no significa “seguro por definición”
Esta es la parte que ahora escribiría con más precisión. Una arquitectura estática elimina muchos componentes server-side del camino público, pero no elimina el trabajo de seguridad.
- Un archivo expuesto sigue expuesto. Si
.git, un.env, un backup o un secreto termina dentro del directorio público, Nginx puede servirlo tan eficientemente como un HTML si la configuración no lo bloquea. - Nginx, TLS, el sistema operativo y la red siguen formando parte de la superficie de ataque. Hay que mantenerlos actualizados y bien configurados.
- El código del cliente también puede tener vulnerabilidades. HTML estático no vuelve seguros automáticamente JavaScript, la lógica de autenticación en el navegador ni los scripts de terceros.
- El agotamiento de recursos sigue siendo posible. Conexiones, ancho de banda, handshakes TLS, workers, escritura de logs y espacio en disco son finitos. Suficiente tráfico también puede afectar a un sitio estático.
Por eso la conclusión correcta no es “estas requests no importan”, sino: muchas sondas específicas de aplicaciones no encuentran nada que ejecutar en esta arquitectura.
“Miles de escaneos al día” puede sonar peor de lo que realmente carga
Los logs amplifican visualmente el ruido de Internet porque cada prueba fallida aparece como una línea. Pero un total diario no basta para saber si el tráfico es operacionalmente importante. Varios miles de requests distribuidos durante 24 horas son muy distintos de la misma cantidad concentrada en un pico corto.
La CPU también es solo una señal. En mi caso se mantuvo estable, lo que sí es una evidencia útil de que este tráfico no estaba generando una presión de cómputo visible en ese momento. No demuestra que el coste fuera cero en todo lo demás. Para evaluarlo bien también miraría tasa de requests, ancho de banda, conexiones activas, workers de Nginx, latencia, volumen de logs y uso de disco.
Lo que aprendí observando este ruido
La lección más importante para mí es que la arquitectura puede volver irrelevantes categorías enteras de ataques automatizados sin crear una regla para cada firma de scanner. Un bot puede buscar backdoors de WordPress y PHP todo el día; si el camino público no contiene WordPress ni PHP, esas sondas tienen muy poco que aprovechar.
Pero ya no confundo “irrelevante para mi stack” con “tráfico inocuo”. Mi lista mental actual es sencilla:
- Saber qué runtimes y servicios son realmente alcanzables desde el camino público.
- Mantener secretos, metadatos del repositorio, backups y artefactos de build fuera del web root o bloquearlos explícitamente.
- Tratar las sondas 404 repetitivas como ruido normal de Internet mientras no haya otras señales de targeting o impacto.
- No decidir que el tráfico abusivo es gratis mirando solo la CPU.
- Mantener actualizados Nginx, el sistema operativo, las bibliotecas TLS y las dependencias aunque la aplicación sea estática.
Lo que esta experiencia no demuestra
No hice un benchmark controlado ni probé un ataque de denegación de servicio. Solo observé los logs de producción y la carga del servidor mientras llegaban miles de requests de scanners cada día. Eso basta para una conclusión práctica sobre mi configuración, pero no para una regla universal de que “lo estático siempre gana”.
Lo que sí puedo afirmar con precisión es esto: en mi export estático de Next.js servido por Nginx, el tráfico de scanners que vi terminaba en gran parte como file misses y 404 baratos en lugar de llegar a un runtime PHP o a una aplicación con base de datos. La carga de CPU permaneció igual mientras lo observaba.
Ese es el tipo de resiliencia que me interesa. Tener menos piezas no vuelve invulnerable a un sistema. Simplemente deja menos lugares donde el ruido aleatorio de Internet pueda convertirse en trabajo real de la aplicación.