Vicen Moreno

Pro Googler

Follow me on GitHub

WordPress y Core Web Vitals - Optimización real para SEO

Google penaliza sitios lentos. Tu WordPress probablemente es lento.

El problema

Cliente con WordPress:

  • LCP: 4.2s (malo, debería ser <2.5s)
  • FID: 180ms (malo, debería ser <100ms)
  • CLS: 0.25 (malo, debería ser <0.1)

Google Search Console mostraba “Poor Core Web Vitals”. Rankings cayendo.

Core Web Vitals explicados

LCP (Largest Contentful Paint): Tiempo hasta que el contenido principal es visible.

FID (First Input Delay): Tiempo hasta que la página responde a interacción.

CLS (Cumulative Layout Shift): Cuánto “salta” el contenido mientras carga.

Diagnóstico

# PageSpeed Insights
https://pagespeed.web.dev/

# Lighthouse en Chrome DevTools
F12 → Lighthouse → Analyze

# Web Vitals extension
Chrome Web Store → Web Vitals

Problemas encontrados:

  1. Imágenes sin lazy load ni dimensiones
  2. JavaScript bloqueante
  3. Fonts cargando tarde
  4. Sin caché de servidor
  5. Hosting lento

Optimización 1: Imágenes

Lazy loading nativo

// functions.php
// WordPress 5.5+ tiene lazy loading automático
// Pero asegurarse de que está habilitado:
add_filter('wp_lazy_loading_enabled', '__return_true');

Dimensiones explícitas (evita CLS)

// Asegurar que imágenes tienen width/height
add_filter('wp_get_attachment_image_attributes', function($attr, $attachment) {
    if (!isset($attr['width']) || !isset($attr['height'])) {
        $metadata = wp_get_attachment_metadata($attachment->ID);
        if ($metadata) {
            $attr['width'] = $metadata['width'];
            $attr['height'] = $metadata['height'];
        }
    }
    return $attr;
}, 10, 2);

WebP automático

# Instalar plugin: WebP Express o ShortPixel
# Convierte imágenes a WebP automáticamente

Preload de LCP image

// Preload de la imagen hero
add_action('wp_head', function() {
    if (is_front_page()) {
        echo '<link rel="preload" as="image" href="' . get_theme_file_uri('/images/hero.webp') . '">';
    }
}, 1);

Resultado LCP: 4.2s → 2.1s

Optimización 2: JavaScript

Diferir scripts no críticos

// functions.php
add_filter('script_loader_tag', function($tag, $handle, $src) {
    $defer_scripts = ['jquery-migrate', 'wp-embed', 'comment-reply'];

    if (in_array($handle, $defer_scripts)) {
        return str_replace(' src', ' defer src', $tag);
    }

    return $tag;
}, 10, 3);

Eliminar jQuery si no es necesario

// Si tu tema no necesita jQuery
add_action('wp_enqueue_scripts', function() {
    if (!is_admin()) {
        wp_deregister_script('jquery');
    }
}, 100);

Inline critical CSS

// Inline CSS crítico para above-the-fold
add_action('wp_head', function() {
    ?>
    <style id="critical-css">
        /* CSS mínimo para renderizar above the fold */
        body { margin: 0; font-family: system-ui, sans-serif; }
        .header { background: #fff; padding: 1rem; }
        .hero { min-height: 50vh; }
        /* ... resto de CSS crítico */
    </style>
    <?php
}, 1);

// Cargar resto de CSS de forma asíncrona
add_filter('style_loader_tag', function($tag, $handle) {
    if ($handle === 'theme-style') {
        return str_replace(
            "rel='stylesheet'",
            "rel='preload' as='style' onload=\"this.onload=null;this.rel='stylesheet'\"",
            $tag
        );
    }
    return $tag;
}, 10, 2);

Resultado FID: 180ms → 45ms

Optimización 3: Fonts

Preconnect a Google Fonts

add_action('wp_head', function() {
    echo '<link rel="preconnect" href="https://fonts.googleapis.com">';
    echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
}, 1);

Font-display: swap

// Si usas Google Fonts
add_filter('style_loader_tag', function($tag, $handle) {
    if (strpos($handle, 'google-fonts') !== false) {
        return str_replace('&display=auto', '&display=swap', $tag);
    }
    return $tag;
}, 10, 2);

Self-host fonts (mejor opción)

/* style.css - Fonts locales */
@font-face {
    font-family: 'Inter';
    src: url('/fonts/inter-var.woff2') format('woff2');
    font-weight: 100 900;
    font-display: swap;
}

Optimización 4: Caché

Page caching con WP Super Cache

// wp-config.php
define('WP_CACHE', true);

Configuración:

Settings → WP Super Cache
→ Caching: ON
→ Cache Delivery Method: Expert
→ Preload: Enable

Object caching con Redis

# Instalar Redis
sudo apt install redis-server

# Plugin: Redis Object Cache
wp plugin install redis-cache --activate
wp redis enable
// wp-config.php
define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);

Browser caching via .htaccess

# .htaccess
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>

<IfModule mod_headers.c>
    <FilesMatch "\.(ico|pdf|jpg|jpeg|png|webp|gif|js|css|woff2)$">
        Header set Cache-Control "max-age=31536000, public"
    </FilesMatch>
</IfModule>

Optimización 5: Hosting

Problema

Hosting compartido barato: TTFB de 800ms.

Solución

Migrar a hosting optimizado para WordPress:

  • Cloudways (DigitalOcean)
  • Kinsta
  • WP Engine

O usar CDN:

Cloudflare (gratis):
- CDN global
- Caché de assets
- HTTP/3
- Brotli compression

Resultado TTFB: 800ms → 120ms

Optimización 6: CLS específico

Reservar espacio para ads

.ad-container {
    min-height: 250px; /* Altura del ad */
    background: #f0f0f0;
}

Reservar espacio para imágenes

.image-container {
    aspect-ratio: 16 / 9;
    background: #f0f0f0;
}

Evitar inserción dinámica de contenido

// MAL: Insertar contenido arriba que empuja todo
document.body.insertBefore(newElement, document.body.firstChild);

// BIEN: Insertar al final o en contenedor reservado
document.getElementById('dynamic-content').appendChild(newElement);

Resultado CLS: 0.25 → 0.05

Monitoreo continuo

Google Search Console

Search Console → Core Web Vitals
→ Ver URLs con problemas
→ Monitorear mejoras

Real User Monitoring (RUM)

// Enviar métricas reales a Google Analytics
add_action('wp_footer', function() {
    ?>
    <script>
    // Web Vitals library
    import {onLCP, onFID, onCLS} from 'web-vitals';

    function sendToAnalytics({name, value, id}) {
        gtag('event', name, {
            event_category: 'Web Vitals',
            value: Math.round(name === 'CLS' ? value * 1000 : value),
            event_label: id,
            non_interaction: true,
        });
    }

    onLCP(sendToAnalytics);
    onFID(sendToAnalytics);
    onCLS(sendToAnalytics);
    </script>
    <?php
});

Resultados finales

Métrica Antes Después Objetivo
LCP 4.2s 1.8s <2.5s ✅
FID 180ms 45ms <100ms ✅
CLS 0.25 0.05 <0.1 ✅
TTFB 800ms 120ms <200ms ✅

Impacto SEO:

  • Rankings: +15 posiciones promedio
  • Tráfico orgánico: +40%
  • Bounce rate: -25%

Checklist de optimización

  • Lazy load en imágenes
  • Dimensiones explícitas en imágenes
  • Formato WebP
  • Preload de LCP image
  • JavaScript diferido
  • Critical CSS inline
  • Fonts con display:swap
  • Page caching habilitado
  • Object caching (Redis)
  • CDN configurado
  • Espacio reservado para ads
  • Monitoreo RUM activo

¿Has optimizado Core Web Vitals en WordPress? ¿Qué técnicas te dieron mejores resultados?


 Anterior      Posterior

Por Vicente José Moreno Escobar el 20 de agosto de 2024
Archivado en: WordPress   Performance   SEO



Puedes disfrutar de otros artículos como éste en el archivo del sitio.