Como aprovechar al máximo tu servidor

Este post comienza con la inquietud de migrar un blog que recibe hasta 100,000 visitas diarias, es decir que cuando se pública en esta red, genera hasta 1,000 requests por segundo, dependiendo la nota y el título, muchas de estas veces, se llegaba hasta 2,900 visitas simultáneas en tiempo real en Google Analytics usando WordPress y Mediatemple.

El punto es que al comenzar y hace un propio CMS para poder tener un control total de la comunidad sin atenernos a Buddypress, Cubepoints y otros plugins que lo que hacen es quitarte visitas saturando la base de datos.

Cual fue mi sorpresa al poner la app en producción, anunciarla y ver que se cayera y el load average subiera por los cielos con tan solo 50 visitas simultaneas:

Esto fue difícil de asimilar, porque un server de 1 GB en RAM de Amazon EC2 solo soporta 50 visitas.

Después de indagar, aprendí que esto se debe a que el sitio no debería estar generando vistas ni queries cada que tiene visitas, esto utiliza mucho CPU y RAM en. Por otro lado, estaba usando Apache2, y nginx tiene mejor performance según las gráficas.

apachevsnginx

Lo comprobé  haciendo el experimento nuevamente y podemos observar que logra soportar más de 100 usuarios simultáneos, definitivamente nginx funciona mejor que apache2 para optimizar recursos del server.

Ahora después de todo esto, el server se colgó de nuevo, ¿Pero que pasa?

Lo que pasa es que no se esta usando la tecnología de Cache correctamente, esto lo que hace, al primer visitante genera el query a la base de datos y el render de la vista, pero si cacheamos el query y la vista, a la segunda visita ya no va a hacer ningún query. Entonces observemos las vistas y veamos su performance:

laravelcache

Eso hace muchos queries, apesar de usar eager loading, eso sobrecalienta los servers de la base de datos y se cuelga debido a que ocupa 15.25MB en RAM del servidor cargar esto y distribuirlo porque renderea la vista y todos los querys.

Pero si esto lo cacheamos correctamente, podremos ahorrar quizá la mitad de recursos del servidor y poder repartir la carga con Amazon AWS ElastiCache.

Así que ya agregue un método en los controllers para poder cachear tanto los queries como la vista completa para reducir considerablemente el tiempo de carga:

priemracarga

Como podemos ver se redució considerablemente el tiempo de carga de 1,140 milisegundos a 211.25 milsegundos, es decir = 928.75 milisegundos menos.  Esto quiere decir que las siguientes cargas durante 60 minutos van a ser cacheada hasta que pasen estos 60 minutos la próxima carga del request que siga.

Para el código:

 public function viewPost($slug)
{
if(\Cache::has('post-'.$slug)) { //Se checa si no hay ya algo cacheado
$post = Cache::get('post-'. $slug); // Si si, entonces cargarlo y retornalo
return $post;
}
Cache::store('redis')->put('post-'.$slug, $this->cachePost($slug), 60); //Si no se encuentra entonces guardalo
$post = Cache::get('post-'. $slug);
return $post;
}

Y la funcion de cachePost()


<span style="line-height: 1.7;">public function cachePost($slug) </span><span style="line-height: 1.7;">{</span>

$post = \App\models\Post::where('slug', $slug)->with('user')->first();
if($post->type == 'video')
{
$info = Embed::create($post->featured_media);
$post->featured_media = $info->code;
$post->featured = $info->image;
$info->width = 650;
return \View::make('frontend.post', compact('post'))->render();
}
return \View::make('frontend.post', compact('post'))->render();
}

Y esta es otra vista que muestra videos, cargaba muy lento porque tenia muchos queries y vista, vean ahora como quedo:

otravista

Ahora viene la prueba final, hacer el commit y push para despues darle pull en el servidor y configurar .env las variables environment para usar Amazon AWS ElastiCache y redis y hagamos la prueba en la página que se nos caía cuando se hacían muchos requests:

ramsuccess

Podemos observar que de 542 milisegundos que tomó abrir la primera vez que no estaba cacheada, bajo a 83 milisegundos, y el ram disminuyó casi ¡20 megas en RAM!
Cachear es genial, pero ahora debo setear los tiempos para esto, que incremente, a menos que se edite o un comentario sea agregado.

Hagamos la prueba con tráfico para ver como se comporta y si puede tolerar más visitas de las de antes:

Como podemos observar ya soporta requests recurrentes ocupando menos CPU y menos RAM. Lo cual nos permite estabilizar el servidor y lograr economizar más en Elastic Beasntalk.

Crear SSH keys en Windows y usarlas con DigitalOcean y Laravel Forge

Antes que nada este es un diario donde me recuerdo cosas que se me dificultan memorizar y por otro lado se que puede ayudar a muchos que están aprendiendo.

  1. Primero que nada creamos una cuenta en DigitalOcean y pusimos la API Key en Laravel Forge.
  2. Una vez conectado, creamos una instancia la cual automáticamente Laravel configura para que tenga buenos cimientos para una aplicación Laravel.
  3. Una vez teniendo el servidor creado, notaremos que si intentamos ingresar, este nos va a rebotar una y otra vez, esto es debido a que esta esperando una SSH Key para poder accesar.
  4. Para crear la SSH Key en Windows, es necesario bajar el PuttyGen y crear una nueva key en el formato SSH-2 RSA key, una vez completado pueden poner una contrasena o guardar el Private Key como ppk y el Public key como pub.
  5. Teniendo este archivo, necesitan ingresar a Putty y configurar la sesion. Vayan a Putty Configuration y a la opcion dentro de SSH que dice +Auth. Una vez ahi denle en browse y escojan la private key (ppk).
  6. Configuren el servidor como cualquier otro. Si intentan conectarse veran que los vuelve a rebotar, eso es porque aun no hemos agregado a Laravel Forge SSH Key la key generada que comienza: ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAiCnCkFakZQEcL/AhhcKs650x3THEBtjO9mrAnjbCZxI9/V…………………….
  7. Vayan a la pagina principal del servidor que hicieron deployment y seleccionen la pestana de SSH keys, escojan cualquier nombre y peguen la SSH key en texto en el Text Block y guarden.

Ahora si intentan conectarse todo funcionara a la perfección.

Accesar logs en tiempo real via terminal SSH

Estos comandos son realmente utiles para monitorear los logs(registros de lo que sucede en tu servidor) de los siguientes:

  • Apache access log: /usr/local/apache/logs/access_log
  • Apache error log: /usr/local/apache/logs/error_log
  • Domlogs: /usr/local/apache/domlogs
  • FTP/Server messages log: /var/log/messages
  • cPanel access log: /usr/local/cpanel/logs/access_log
  • cPanel error log: /usr/local/cpanel/logs/error_log

Estos se pueden accesar con el siguiente comando:

tail -f /usr/local/apache/logs/access_log

 

Ahi usen las carpetas de los diferentes logs y podran ver reportes en tiempo real de todos los que hacen GET , POST y mas REQUESTS.

Hacer benchmark del tiempo de carga y desempeño de tu sitio con Apache

Apache tiene una excelente herramienta para benchmark llamada “ab”.
Este es el comando que se utiliza para poder hacer pruebas duras a tu servidor y desempeño de PHP y en caso de que hayas instalado un OP Code Cache.

Simplemente typea lo siguiente en tu root SSH.

ab -n 1000 -c 10 http://[YOURSITE.COM]/

Este es un ejemplo prueba que obtuve en esa prueba.

Server Software: nginx
Server Hostname: cerebrodigital.org
Server Port: 80
Document Path: /
Document Length: 128553 bytes
Concurrency Level: 10
Time taken for tests: 1.345299 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 129042914 bytes
HTML transferred: 128681553 bytes
Requests per second: 743.33 [#/sec] (mean)
Time per request: 13.453 [ms] (mean)
Time per request: 1.345 [ms] (mean, across all concurrent requests)
Transfer rate: 93672.86 [Kbytes/sec] received
Connection Times (ms)
 min mean[+/-sd] median max
Connect: 0 0 0.3 0 6
Processing: 5 12 3.2 13 25
Waiting: 5 10 2.6 11 19
Total: 5 12 3.2 13 25
Percentage of the requests served within a certain time (ms)
 50% 13
 66% 14
 75% 15
 80% 15
 90% 17
 95% 18
 98% 20
 99% 23
 100% 25 (longest request)
root@host [~]#

Bloquear Robots Malos (Roban recursos) via .htaccess

Últimamente he tenido muchos problemas con los recursos de mi servidor. Simplemente llegaba al limit de httprequests y apache2 comenzaba a fallar. Mi cabeza daba vueltas una y otra vez. Soporte del Datacenter no encontraba la solución adecuada, siempre eran alternativas pasajeras. Hasta que me decidí a ver qué era lo que sucedía.

Fue demasiada sorpresa al descubrir que mis domlogs excedían en conexiones GET de diferentes spiders de todo el mundo. Pero, realmente estas conexiones robaban demasiados procesos?
Si, los robaban ya que los bots hacen httprequests asi como los mismos humanos. Descargan todo el documento para poder navegar hrefs e imágenes.

Cuál es la solución a este problema?

Bloquearlos por medio de robots.txt pero también existe un método más rápido y eficiente. Bloquearlos con .htaccess.

Los comandos para monitorear esto via comandos POSIX (SSH) :

Ver como van los logs de todo el servidor de todos los GET:
tail -f /usr/local/apache/domlogs/*

Para ver como va una pagina en particular:
tail -f /usr/local/apache/domlogs/dominio.com

Desempeno del servidor:
nice top -c

CODIGO PARA BLOQUEAR BOTS EN .HTACCESS

SetEnvIfNoCase User-Agent ^$ bad_bot
SetEnvIfNoCase User-Agent "^AESOP_com_SpiderMan" bad_bot
SetEnvIfNoCase User-Agent "^Alexibot" bad_bot
SetEnvIfNoCase User-Agent "Anonymouse.org" bad_bot
SetEnvIfNoCase User-Agent "^asterias" bad_bot
SetEnvIfNoCase User-Agent "^attach" bad_bot
SetEnvIfNoCase User-Agent "^BackDoorBot" bad_bot
SetEnvIfNoCase User-Agent "^BackWeb" bad_bot
SetEnvIfNoCase User-Agent "Bandit" bad_bot
SetEnvIfNoCase User-Agent "^Baiduspider" bad_bot
SetEnvIfNoCase User-Agent "^BatchFTP" bad_bot
SetEnvIfNoCase User-Agent "^Bigfoot" bad_bot
SetEnvIfNoCase User-Agent "^Black.Hole" bad_bot
SetEnvIfNoCase User-Agent "^BlackWidow" bad_bot
SetEnvIfNoCase User-Agent "^BlowFish" bad_bot
SetEnvIfNoCase User-Agent "^Bot mailto:craftbot@yahoo.com" bad_bot
SetEnvIfNoCase User-Agent "^BotALot" bad_bot
SetEnvIfNoCase User-Agent "Buddy" bad_bot
SetEnvIfNoCase User-Agent "^BuiltBotTough" bad_bot
SetEnvIfNoCase User-Agent "^Bullseye" bad_bot
SetEnvIfNoCase User-Agent "^BunnySlippers" bad_bot
SetEnvIfNoCase User-Agent "^Cegbfeieh" bad_bot
SetEnvIfNoCase User-Agent "^CheeseBot" bad_bot
SetEnvIfNoCase User-Agent "^CherryPicker" bad_bot
SetEnvIfNoCase User-Agent "^ChinaClaw" bad_bot
SetEnvIfNoCase User-Agent "Collector" bad_bot
SetEnvIfNoCase User-Agent "Copier" bad_bot
SetEnvIfNoCase User-Agent "^CopyRightCheck" bad_bot
SetEnvIfNoCase User-Agent "^cosmos" bad_bot
SetEnvIfNoCase User-Agent "^Crescent" bad_bot
SetEnvIfNoCase User-Agent "^Curl" bad_bot
SetEnvIfNoCase User-Agent "^Custo" bad_bot
SetEnvIfNoCase User-Agent "^DA" bad_bot
SetEnvIfNoCase User-Agent "^DISCo" bad_bot
SetEnvIfNoCase User-Agent "^DIIbot" bad_bot
SetEnvIfNoCase User-Agent "^DittoSpyder" bad_bot
SetEnvIfNoCase User-Agent "^Download" bad_bot
SetEnvIfNoCase User-Agent "^Download Demon" bad_bot
SetEnvIfNoCase User-Agent "^Download Devil" bad_bot
SetEnvIfNoCase User-Agent "^Download Wonder" bad_bot
SetEnvIfNoCase User-Agent "Downloader" bad_bot
SetEnvIfNoCase User-Agent "^dragonfly" bad_bot
SetEnvIfNoCase User-Agent "^Drip" bad_bot
SetEnvIfNoCase User-Agent "^eCatch" bad_bot
SetEnvIfNoCase User-Agent "^EasyDL" bad_bot
SetEnvIfNoCase User-Agent "^ebingbong" bad_bot
SetEnvIfNoCase User-Agent "^EirGrabber" bad_bot
SetEnvIfNoCase User-Agent "^EmailCollector" bad_bot
SetEnvIfNoCase User-Agent "^EmailSiphon" bad_bot
SetEnvIfNoCase User-Agent "^EmailWolf" bad_bot
SetEnvIfNoCase User-Agent "^EroCrawler" bad_bot
SetEnvIfNoCase User-Agent "^Exabot" bad_bot
SetEnvIfNoCase User-Agent "^Express WebPictures" bad_bot
SetEnvIfNoCase User-Agent "Extractor" bad_bot
SetEnvIfNoCase User-Agent "^EyeNetIE" bad_bot
SetEnvIfNoCase User-Agent "^FileHound" bad_bot
SetEnvIfNoCase User-Agent "^FlashGet" bad_bot
SetEnvIfNoCase User-Agent "^Foobot" bad_bot
SetEnvIfNoCase User-Agent "^flunky" bad_bot
SetEnvIfNoCase User-Agent "^FrontPage" bad_bot
SetEnvIfNoCase User-Agent "^GetRight" bad_bot
SetEnvIfNoCase User-Agent "^GetSmart" bad_bot
SetEnvIfNoCase User-Agent "^GetWeb!" bad_bot
SetEnvIfNoCase User-Agent "^Go!Zilla" bad_bot
SetEnvIfNoCase User-Agent "Google Wireless Transcoder" bad_bot
SetEnvIfNoCase User-Agent "^Go-Ahead-Got-It" bad_bot
SetEnvIfNoCase User-Agent "^gotit" bad_bot
SetEnvIfNoCase User-Agent "Grabber" bad_bot
SetEnvIfNoCase User-Agent "^GrabNet" bad_bot
SetEnvIfNoCase User-Agent "^Grafula" bad_bot
SetEnvIfNoCase User-Agent "^Harvest" bad_bot
SetEnvIfNoCase User-Agent "^hloader" bad_bot
SetEnvIfNoCase User-Agent "^HMView" bad_bot
SetEnvIfNoCase User-Agent "^httplib" bad_bot
SetEnvIfNoCase User-Agent "^HTTrack" bad_bot
SetEnvIfNoCase User-Agent "^humanlinks" bad_bot
SetEnvIfNoCase User-Agent "^ia_archiver" bad_bot
SetEnvIfNoCase User-Agent "^IlseBot" bad_bot
SetEnvIfNoCase User-Agent "^Image Stripper" bad_bot
SetEnvIfNoCase User-Agent "^Image Sucker" bad_bot
SetEnvIfNoCase User-Agent "Indy Library" bad_bot
SetEnvIfNoCase User-Agent "^InfoNaviRobot" bad_bot
SetEnvIfNoCase User-Agent "^InfoTekies" bad_bot
SetEnvIfNoCase User-Agent "^Intelliseek" bad_bot
SetEnvIfNoCase User-Agent "^InterGET" bad_bot
SetEnvIfNoCase User-Agent "^Internet Ninja" bad_bot
SetEnvIfNoCase User-Agent "^Iria" bad_bot
SetEnvIfNoCase User-Agent "^Jakarta" bad_bot
SetEnvIfNoCase User-Agent "^JennyBot" bad_bot
SetEnvIfNoCase User-Agent "^JetCar" bad_bot
SetEnvIfNoCase User-Agent "^JOC" bad_bot
SetEnvIfNoCase User-Agent "^JustView" bad_bot
SetEnvIfNoCase User-Agent "^Jyxobot" bad_bot
SetEnvIfNoCase User-Agent "^Kenjin.Spider" bad_bot
SetEnvIfNoCase User-Agent "^Keyword.Density" bad_bot
SetEnvIfNoCase User-Agent "^larbin" bad_bot
SetEnvIfNoCase User-Agent "^LeechFTP" bad_bot
SetEnvIfNoCase User-Agent "^LexiBot" bad_bot
SetEnvIfNoCase User-Agent "^lftp" bad_bot
SetEnvIfNoCase User-Agent "^libWeb/clsHTTP" bad_bot
SetEnvIfNoCase User-Agent "^likse" bad_bot
SetEnvIfNoCase User-Agent "^LinkextractorPro" bad_bot
SetEnvIfNoCase User-Agent "^LinkScan/8.1a.Unix" bad_bo
SetEnvIfNoCase User-Agent "^LNSpiderguy" bad_bott
SetEnvIfNoCase User-Agent "^LinkWalker" bad_bot
SetEnvIfNoCase User-Agent "^lwp-trivial" bad_bot
SetEnvIfNoCase User-Agent "^LWP::Simple" bad_bot
SetEnvIfNoCase User-Agent "^Magnet" bad_bot
SetEnvIfNoCase User-Agent "^Mag-Net" bad_bot
SetEnvIfNoCase User-Agent "^MarkWatch" bad_bot
SetEnvIfNoCase User-Agent "^Mass Downloader" bad_bot
SetEnvIfNoCase User-Agent "^Mata.Hari" bad_bot
SetEnvIfNoCase User-Agent "^Memo" bad_bot
SetEnvIfNoCase User-Agent "^Microsoft.URL" bad_bot
SetEnvIfNoCase User-Agent "^Microsoft URL Control" bad_bot
SetEnvIfNoCase User-Agent "^MIDown tool" bad_bot
SetEnvIfNoCase User-Agent "^MIIxpc" bad_bot
SetEnvIfNoCase User-Agent "^Mirror" bad_bot
SetEnvIfNoCase User-Agent "^Missigua Locator" bad_bot
SetEnvIfNoCase User-Agent "^Mister PiX" bad_bot
SetEnvIfNoCase User-Agent "^moget" bad_bot
SetEnvIfNoCase User-Agent "^Mozilla/3.Mozilla/2.01" bad_bot
SetEnvIfNoCase User-Agent "^Mozilla.*NEWT" bad_bot
SetEnvIfNoCase User-Agent "^NAMEPROTECT" bad_bot
SetEnvIfNoCase User-Agent "^Navroad" bad_bot
SetEnvIfNoCase User-Agent "^NearSite" bad_bot
SetEnvIfNoCase User-Agent "^NetAnts" bad_bot
SetEnvIfNoCase User-Agent "^Netcraft" bad_bot
SetEnvIfNoCase User-Agent "^NetMechanic" bad_bot
SetEnvIfNoCase User-Agent "^NetSpider" bad_bot
SetEnvIfNoCase User-Agent "^Net Vampire" bad_bot
SetEnvIfNoCase User-Agent "^NetZIP" bad_bot
SetEnvIfNoCase User-Agent "^NextGenSearchBot" bad_bot
SetEnvIfNoCase User-Agent "^NG" bad_bot
SetEnvIfNoCase User-Agent "^NICErsPRO" bad_bot
SetEnvIfNoCase User-Agent "^NimbleCrawler" bad_bot
SetEnvIfNoCase User-Agent "^Ninja" bad_bot
SetEnvIfNoCase User-Agent "^NPbot" bad_bot
SetEnvIfNoCase User-Agent "^Octopus" bad_bot
SetEnvIfNoCase User-Agent "^Offline Explorer" bad_bot
SetEnvIfNoCase User-Agent "^Offline Navigator" bad_bot
SetEnvIfNoCase User-Agent "^Openfind" bad_bot
SetEnvIfNoCase User-Agent "^OutfoxBot" bad_bot
SetEnvIfNoCase User-Agent "^PageGrabber" bad_bot
SetEnvIfNoCase User-Agent "^Papa Foto" bad_bot
SetEnvIfNoCase User-Agent "^pavuk" bad_bot
SetEnvIfNoCase User-Agent "^pcBrowser" bad_bot
SetEnvIfNoCase User-Agent "^PHP version tracker" bad_bot
SetEnvIfNoCase User-Agent "^Pockey" bad_bot
SetEnvIfNoCase User-Agent "^ProPowerBot/2.14" bad_bot
SetEnvIfNoCase User-Agent "^ProWebWalker" bad_bot
SetEnvIfNoCase User-Agent "^psbot" bad_bot
SetEnvIfNoCase User-Agent "^Pump" bad_bot
SetEnvIfNoCase User-Agent "^QueryN.Metasearch" bad_bot
SetEnvIfNoCase User-Agent "^RealDownload" bad_bot
SetEnvIfNoCase User-Agent "Reaper" bad_bot
SetEnvIfNoCase User-Agent "Recorder" bad_bot
SetEnvIfNoCase User-Agent "^ReGet" bad_bot
SetEnvIfNoCase User-Agent "^RepoMonkey" bad_bot
SetEnvIfNoCase User-Agent "^RMA" bad_bot
SetEnvIfNoCase User-Agent "Siphon" bad_bot
SetEnvIfNoCase User-Agent "sitecheck.internetseer.com" bad_bot
SetEnvIfNoCase User-Agent "^SiteSnagger" bad_bot
SetEnvIfNoCase User-Agent "^SlySearch" bad_bot
SetEnvIfNoCase User-Agent "^SmartDownload" bad_bot
SetEnvIfNoCase User-Agent "^Snake" bad_bot
SetEnvIfNoCase User-Agent "^Snapbot" bad_bot
SetEnvIfNoCase User-Agent "^Snoopy" bad_bot
SetEnvIfNoCase User-Agent "^sogou" bad_bot
SetEnvIfNoCase User-Agent "^SpaceBison" bad_bot
SetEnvIfNoCase User-Agent "^SpankBot" bad_bot
SetEnvIfNoCase User-Agent "^spanner" bad_bot
SetEnvIfNoCase User-Agent "^Sqworm" bad_bot
SetEnvIfNoCase User-Agent "Stripper" bad_bot
SetEnvIfNoCase User-Agent "Sucker" bad_bot
SetEnvIfNoCase User-Agent "^SuperBot" bad_bot
SetEnvIfNoCase User-Agent "^SuperHTTP" bad_bot
SetEnvIfNoCase User-Agent "^Surfbot" bad_bot
SetEnvIfNoCase User-Agent "^suzuran" bad_bot
SetEnvIfNoCase User-Agent "^Szukacz/1.4" bad_bot
SetEnvIfNoCase User-Agent "^tAkeOut" bad_bot
SetEnvIfNoCase User-Agent "^Teleport" bad_bot
SetEnvIfNoCase User-Agent "^Telesoft" bad_bot
SetEnvIfNoCase User-Agent "^TurnitinBot/1.5" bad_bot
SetEnvIfNoCase User-Agent "^The.Intraformant" bad_bot
SetEnvIfNoCase User-Agent "^TheNomad" bad_bot
SetEnvIfNoCase User-Agent "^TightTwatBot" bad_bot
SetEnvIfNoCase User-Agent "^Titan" bad_bot
SetEnvIfNoCase User-Agent "^toCrawl/UrlDispatcher" bad_bot
SetEnvIfNoCase User-Agent "^True_Robot" bad_bot
SetEnvIfNoCase User-Agent "^turingos" bad_bot
SetEnvIfNoCase User-Agent "^TurnitinBot" bad_bot
SetEnvIfNoCase User-Agent "^URLy.Warning" bad_bot
SetEnvIfNoCase User-Agent "^Vacuum" bad_bot
SetEnvIfNoCase User-Agent "^VCI" bad_bot
SetEnvIfNoCase User-Agent "^VoidEYE" bad_bot
SetEnvIfNoCase User-Agent "^Web Image Collector" bad_bot
SetEnvIfNoCase User-Agent "^Web Sucker" bad_bot
SetEnvIfNoCase User-Agent "^WebAuto" bad_bot
SetEnvIfNoCase User-Agent "^WebBandit" bad_bot
SetEnvIfNoCase User-Agent "^Webclipping.com" bad_bot
SetEnvIfNoCase User-Agent "^WebCopier" bad_bot
SetEnvIfNoCase User-Agent "^WebEMailExtrac.*" bad_bot
SetEnvIfNoCase User-Agent "^WebEnhancer" bad_bot
SetEnvIfNoCase User-Agent "^WebFetch" bad_bot
SetEnvIfNoCase User-Agent "^WebGo IS" bad_bot
SetEnvIfNoCase User-Agent "^Web.Image.Collector" bad_bot
SetEnvIfNoCase User-Agent "^WebLeacher" bad_bot
SetEnvIfNoCase User-Agent "^WebmasterWorldForumBot" bad_bot
SetEnvIfNoCase User-Agent "^WebReaper" bad_bot
SetEnvIfNoCase User-Agent "^WebSauger" bad_bot
SetEnvIfNoCase User-Agent "^WebSite" bad_bot
SetEnvIfNoCase User-Agent "^Website eXtractor" bad_bot
SetEnvIfNoCase User-Agent "^Website Quester" bad_bot
SetEnvIfNoCase User-Agent "^Webster" bad_bot
SetEnvIfNoCase User-Agent "^WebStripper" bad_bot
SetEnvIfNoCase User-Agent "^WebWhacker" bad_bot
SetEnvIfNoCase User-Agent "^WebZIP" bad_bot
SetEnvIfNoCase User-Agent "Whacker" bad_bot
SetEnvIfNoCase User-Agent "^Widow" bad_bot
SetEnvIfNoCase User-Agent "^WISENutbot" bad_bot
SetEnvIfNoCase User-Agent "^WWWOFFLE" bad_bot
SetEnvIfNoCase User-Agent "^WWW-Collector-E" bad_bot
SetEnvIfNoCase User-Agent "^Xaldon" bad_bot
SetEnvIfNoCase User-Agent "^Xenu" bad_bot
SetEnvIfNoCase User-Agent "^Zeus" bad_bot
SetEnvIfNoCase User-Agent "^Zyborg" bad_bot

<Limit GET POST HEAD>
Order Allow,Deny
Allow from all
Deny from env=bad_bot
</Limit>

Eso debería funcionar bien. A veces la adición de reglas htaccess que se basan en la dirección IP puede suponer una carga adicional del servidor, pero en este caso se está utilizando la variable User-Agent SetEnvIfNoCase que sólo se ve en la cabecera. No estamos haciendo una busqueda IP, por lo que no debería afectar negativamente al rendimiento.
Ustedes que opinan?