Los bots están por todas partes. Algunos son imprescindibles (p. ej., Googlebot o Bingbot indexan tu web para que aparezca en buscadores), pero otros ralentizan, consumen ancho de banda, raspan contenido para entrenar modelos de IA o buscan vulnerabilidades. Si administras una web o una API, necesitas separar el trigo del grano: permitir lo legítimo y bloquear el abuso antes de que llegue a tu servidor.
A continuación, una guía práctica —con ejemplos y herramientas— para un sitio de tecnología que necesite medidas reales sin romper SEO ni usabilidad.
1) Identificar el tráfico sospechoso
Antes de bloquear, mide:
- Logs del servidor (Apache/Nginx): picos de peticiones repetidas a la misma URL, patrones a
/wp-login.php
o/xmlrpc.php
, user-agents “vacíos” o falsos, ráfagas nocturnas. - Analítica (GA4/Matomo): tasas de rebote anómalas, sesiones de 0 s, países donde no tienes audiencia.
- Latencia y ancho de banda: si el 80/20 no se cumple (mucho consumo con poco valor), hay ruido.
Pistas típicas:
- Picos sobre endpoints de login, búsquedas, RSS, feeds JSON, y /wp-json/wp/v2/users (enumeración de usuarios).
- Descargas masivas de imágenes o PDFs.
- User-agents imitando Google (“Googlebot/2.1”) con IPs que no resuelven a Google.
2) Robots.txt: útil para los buenos, irrelevante para los malos
robots.txt
no bloquea bots maliciosos; orienta a los que sí cumplen. Aun así, configúralo para reducir rastreo innecesario:
User-agent: *
Disallow: /wp-admin/
Disallow: /cgi-bin/
Disallow: /search
Allow: /wp-admin/admin-ajax.php
Sitemap: https://tu-dominio.com/sitemap.xml
Importante: no pongas rutas sensibles en
robots.txt
si no están protegidas por otro medio (auth, firewall). Un bot malicioso las usará como guía.
3) Bloqueos por .htaccess (Apache) o Nginx: puntuales, no la bala de plata
Para bloquear IPs o user-agents concretos en Apache:
# .htaccess
<IfModule mod_authz_core.c>
<RequireAll>
Require all granted
Require not ip 203.0.113.0/24
</RequireAll>
</IfModule>
# Bloqueo por user-agent
BrowserMatchNoCase "curl|python-requests|scrapy|wget" badbot
Order Allow,Deny
Allow from all
Deny from env=badbot
En Nginx:
map $http_user_agent $badbot {
default 0;
~*(curl|python-requests|scrapy|wget) 1;
}
server {
if ($badbot) { return 403; }
deny 203.0.113.0/24;
}
Limitaciones: mantener listas manuales es costoso; los scrapers rotan IPs y UA. Úsalo para casos quirúrgicos, no como estrategia única.
4) WAF (Web Application Firewall): filtra antes de llegar a tu app
Un WAF aplica reglas conocidas para bloquear patrones maliciosos (SQLi, XSS, RFI, bruteforce). Dos enfoques:
4.1 WAF autogestionado (ModSecurity + OWASP CRS)
- ModSecurity (motor) + OWASP ModSecurity Core Rule Set (reglas) es un clásico de eficacia probada.
- Puedes ajustar el Paranoia Level (PL1→PL4) y el Anomaly Threshold para equilibrar seguridad y falsos positivos.
Ejemplo de regla adicional (ModSecurity) para bot UA vacíos:
SecRule REQUEST_HEADERS:User-Agent "^$" \
"id:1000101,phase:1,deny,status:403,msg:'Empty UA blocked'"
4.2 WAF en la nube (Cloudflare, etc.)
- Ventaja: para el tráfico antes de tu servidor (ahorra CPU/IO).
- Reglas por expresiones, reputación IP, país, ASN, user-agent, presencia de JavaScript, etc.
Ejemplo (Cloudflare Firewall Rule):(http.user_agent contains "python-requests") or (ip.geoip.country in {"CN" "RU"} and http.request.uri.path eq "/wp-login.php") → Block
5) Rate limiting y “pruebas de humanidad” sin romper UX
- Rate Limit: limita peticiones por IP/clave/API a endpoints críticos (login, búsqueda, APIs).
- Challenge: usa JavaScript challenges o PoW accesibles (prueba de trabajo ligera) para subir el coste al bot sin fricción excesiva.
- CAPTCHA accesibles: si la fricción es inevitable, evita desafíos visuales intrusivos; ofrecen alternativas centradas en privacidad y con audio conforme a WCAG/EAA.
Consejo: aplica reto sólo en riesgo alto (IP nueva, UA sospechoso, sin cookies previas), no a todo el mundo.
6) Buen trato a los “bots buenos”: verificación y allowlist
- Verifica IPs de buscadores (Googlebot, Bingbot) con DNS reverse y forward-confirmed reverse DNS.
- Mantén una allowlist para integraciones M2M (monitorización, uptime, pagos).
- Sirve sitemaps actualizados y evita bloquear CSS/JS esenciales.
Verificar Googlebot (resumen):
- Haz reverse DNS de la IP → debe devolver un dominio de Google (
.googlebot.com
/.google.com
). - Haz forward DNS de ese hostname → debe volver a la misma IP.
7) Proteger APIs y endpoints “caros”
- Tokens y claves con scopes y expiración corta.
- M2M rate limiting por consumer (clave/API, cliente OAuth).
- Firmas HMAC o mutual TLS donde sea viable.
- CORS estrictos y métodos/orígenes permitidos mínimos.
- Payload limits y validación fuerte de entrada (evita resource exhaustion).
8) Cómo hacerlo con un WAF administrado (ejemplo con RunCloud + ModSecurity)
Si gestionas tus webs con un panel que integra ModSecurity + OWASP CRS (por ejemplo, RunCloud):
- En el dashboard, ve a Firewall.
- Ajusta Paranoia Level (empieza en PL1 y sube gradualmente) y el Anomaly Threshold (más bajo = más estricto).
- Añade reglas personalizadas para permitir/bloquear por IP, país, user-agent o valor de cookie.
- Activa notificaciones y revisa logs de bloqueo (id de regla, endpoint).
- Monitoriza falsos positivos (p. ej., búsquedas internas, plugins de formularios) y crea excepciones precisas (por ruta o parámetro).
Cuando el WAF bloquea, el visitante verá un 403 Forbidden. Tu tarea es equilibrar seguridad y negocio.
9) Monitorización continua (y afinado)
- KPIs: solicitudes bloqueadas, tasa de falsos positivos, consumo de ancho de banda, CPU/IO.
- Alertas: patrones nuevos (rutas no existentes, credential stuffing, scraping agresivo).
- Revisión mensual: actualiza reglas, revisa allowlist y endpoints sensibles.
- Pruebas: ejecuta smoke tests tras cambios de reglas para no romper flujos críticos.
10) Ejemplos “copy & paste”
Bloquear acceso a /wp-login.php
por país (Nginx + GeoIP):
geo $block_country {
default 0;
GB 0; US 0; ES 0; # permitidos
default 1; # resto bloqueados
}
location = /wp-login.php {
if ($block_country) { return 403; }
try_files $uri =404;
include fastcgi_params;
fastcgi_pass php-fpm;
}
Apache: prohibir python-requests
y curl
:
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} "(python-requests|curl)" [NC]
RewriteRule .* - [F]
ModSecurity: limitar xmlrpc.php
:
SecRule REQUEST_URI "@streq /xmlrpc.php" \
"id:1000201,phase:1,t:none,deny,status:403,msg:'xmlrpc blocked'"
Checklist “rápido”
- Logs revisados (UA/IP/endpoints raros).
robots.txt
actualizado (sin rutas sensibles).- Rate limit en login/búsquedas/APIs.
- WAF activo (ModSecurity+OWASP CRS o cloud WAF) y reglas personalizadas.
- Allowlist para bots buenos y servicios M2M.
- Desafíos just-in-time (JS/PoW/CAPTCHA accesible) en riesgo alto.
- Protección de APIs (tokens, scopes, CORS, HMAC/mtls).
- Monitorización y alertas; revisión periódica de falsos positivos.
Preguntas frecuentes
¿Puedo bloquear todos los bots de golpe?
No conviene: perderías SEO y servicios útiles. La clave es diferenciar (WAF + verificación de buscadores + allowlist) y limitar tráfico abusivo con rate limiting y retos en riesgo alto.
¿robots.txt
sirve para frenar a los malos?
No. Es un acuerdo de cortesía para crawlers legítimos. Los malos lo ignoran. Úsalo para orientar a los buenos y combina con WAF/limitadores para el resto.
¿Me bastan bloqueos por IP?
Son temporales: los scrapers rotan IPs/ASN. Mejor combinar IP/UA/país + rate limiting + reputación + retos adaptativos.
¿Cómo evito romper el SEO?
Mantén allowlist de bots buenos verificados (Google, Bing), sitemaps publicados y no bloquees CSS/JS que los bots necesitan para renderizar. Verifica la IP real de los bots (reverse DNS) antes de permitir.
Conclusión
Bloquear “bad bots” no es una única regla mágica: es capas. Empieza por ver lo que pasa, aplica WAF + rate limiting, usa retos sólo donde haga falta y protege bien APIs y endpoints caros. Con monitorización y ajustes periódicos, cortarás el ruido sin sacrificar SEO ni experiencias legítimas.