Optimizando WordPress para alto tráfico - Guía práctica desde trincheras
Cómo pasé un sitio WordPress de 2s a 300ms de carga sin cambiar de hosting
El problema 🔥
Hace unas semanas me llegó un proyecto de rescate: un WordPress recibiendo ~100k visitas/día con tiempos de carga de 2-3 segundos. El cliente estaba considerando migrar a una solución custom, pero el presupuesto no acompañaba.
Spoiler: Lo solucionamos sin cambiar el hosting ni reescribir nada.
Metodología de optimización
No se optimiza lo que no se mide. Empecé con estas herramientas:
- Query Monitor - Para identificar queries lentas
- New Relic - Para profiling de PHP
- GTmetrix - Para métricas del navegador
- Chrome DevTools - Para análisis detallado
Problema 1: Queries N+1 ❌
El culpable más común. Un bucle cargando posts que para cada uno hace queries adicionales:
// ❌ ANTES - N+1 queries
$posts = get_posts(['numberposts' => 20]);
foreach ($posts as $post) {
$author = get_user_by('id', $post->post_author); // Query por post
$thumbnail = get_the_post_thumbnail($post->ID); // Otra query
}
// ✅ DESPUÉS - 1 query
$posts = get_posts([
'numberposts' => 20,
'update_post_meta_cache' => true,
'update_post_term_cache' => true,
]);
// Precargamos thumbnails
if ($posts) {
$post_ids = wp_list_pluck($posts, 'ID');
_prime_post_caches($post_ids, true, true);
}
foreach ($posts as $post) {
// Todo está en caché ahora
$author = get_user_by('id', $post->post_author);
$thumbnail = get_the_post_thumbnail($post->ID);
}
Resultado: De 45 queries a 3 queries. Tiempo reducido en 400ms.
Problema 2: Object Cache inexistente 🗄️
WordPress sin object cache persistente es como conducir con el freno de mano puesto.
Instalando Redis
# En el servidor (Ubuntu)
sudo apt-get install redis-server php-redis
sudo systemctl enable redis-server
sudo systemctl start redis-server
// wp-config.php
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);
define('WP_REDIS_TIMEOUT', 1);
define('WP_REDIS_READ_TIMEOUT', 1);
define('WP_REDIS_DATABASE', 0);
Plugin recomendado: Redis Object Cache
Resultado: Tiempo de respuesta reducido en 800ms.
Problema 3: Imágenes sin optimizar 🖼️
El sitio servía JPEGs de 3MB directamente desde uploads. Imperdonable en 2024.
Solución en múltiples frentes:
1. Conversión automática a WebP
// functions.php
add_filter('wp_generate_attachment_metadata', 'generate_webp_versions');
function generate_webp_versions($metadata) {
$upload_dir = wp_upload_dir();
$file_path = $upload_dir['basedir'] . '/' . $metadata['file'];
if (file_exists($file_path)) {
$webp_path = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $file_path);
$image = imagecreatefromstring(file_get_contents($file_path));
imagewebp($image, $webp_path, 80);
imagedestroy($image);
}
return $metadata;
}
2. Lazy loading nativo
WordPress 5.5+ lo incluye por defecto, pero hay que asegurarse:
add_filter('wp_lazy_loading_enabled', '__return_true');
3. CDN con Cloudflare
Configuración crítica en Cloudflare:
- Polish: Lossless
- Mirage: Activado
- Auto Minify: JS, CSS, HTML
- Brotli: Activado
Resultado: Peso de página reducido de 4.5MB a 800KB.
Problema 4: Plugins innecesarios 🔌
Auditoría de plugins instalados:
Antes: 34 plugins activos
Después: 12 plugins activos
Plugins que eliminé y cómo los reemplacé:
| Plugin eliminado | Reemplazo |
|---|---|
| Contact Form 7 | Formulario HTML + PHP custom |
| WP Super Cache | Redis + Nginx caching |
| Smush | CLI optimization + imagemagick |
| Broken Link Checker | Script cron custom |
Cada plugin inactivo o innecesario añade overhead. Menos es más.
Problema 5: PHP 7.4 🐌
El servidor corría PHP 7.4. Actualizar a PHP 8.2 fue un cambio masivo.
Proceso de actualización:
# Backup primero
sudo mysqldump -u root -p wordpress > backup.sql
sudo tar -czf wordpress-backup.tar.gz /var/www/html
# Actualizar PHP
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get install php8.2-fpm php8.2-mysql php8.2-redis
# Actualizar Nginx config
# Cambiar fastcgi_pass unix:/var/run/php/php7.4-fpm.sock
# Por: unix:/var/run/php/php8.2-fpm.sock
sudo systemctl restart nginx
sudo systemctl restart php8.2-fpm
Resultado: Mejora del 35% en tiempo de ejecución PHP.
Configuración Nginx optimizada 🚀
Mi config final de Nginx para WordPress:
# Cache para assets estáticos
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Cache FastCGI para páginas
fastcgi_cache_path /var/cache/nginx levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=1g;
location ~ \.php$ {
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
# Headers para debugging
add_header X-Cache-Status $upstream_cache_status;
}
Resultados finales 📊
| Métrica | Antes | Después | Mejora |
|---|---|---|---|
| Time to First Byte | 1.8s | 180ms | 90% |
| Fully Loaded | 3.2s | 1.1s | 65% |
| Total Page Size | 4.5MB | 780KB | 82% |
| Requests | 127 | 34 | 73% |
| Database Queries | 89 | 12 | 86% |
Lecciones aprendidas 💡
- Redis es no-negociable para WordPress con tráfico real
- PHP 8.2 debería ser el mínimo hoy día
- Mide antes de optimizar - No asumas dónde está el problema
- Los plugins son convenientes, no gratuitos - Cada uno tiene un coste
- WebP es el presente, no el futuro
Herramientas recomendadas
- Query Monitor: Debug de queries
- Redis Object Cache: Object caching persistente
- WP-CLI: Automatización de tareas
- New Relic / Blackfire: Profiling avanzado
- Cloudflare: CDN y optimización
Conclusión
WordPress puede ser rápido. Muy rápido. Pero requiere conocer cómo funciona bajo el capó y no tener miedo de optimizar.
Este proyecto pasó de estar al borde de una reescritura completa a ser perfectamente viable con WordPress. Total invertido: 3 días de trabajo.
¿Tienes un WordPress lento? Empieza por medir con Query Monitor. Te sorprenderás de lo que encuentres.
¿Preguntas? ¿Tu propia experiencia optimizando WordPress? Me encantaría conocerla.
¡Hasta la próxima! ⚡