WordPress lento en móvil: qué optimizar primero

¿Te fue útil?

Los usuarios móviles no “navegan”. Toleran. Tu sitio WordPress tiene alrededor de dos segundos para demostrar que no les está haciendo perder el tiempo, y luego el pulgar vuelve a los resultados de búsqueda. En escritorio puede parecer que todo va bien porque el escritorio oculta pecados: CPUs más rápidas, redes mejores, expectativas más laxas.

Cuando WordPress va lento en móvil, la gente se desespera: instala tres plugins de caché, comprime todo dos veces, culpa al hosting, culpa al tema, culpa a “Google”. No hagas eso. Trátalo como resolución de problemas en producción: mide, aísla el cuello de botella, arregla la primera limitación, repite.

Guía de diagnóstico rápido (encuentra el cuello de botella rápido)

Si no haces nada más, haz esto. La lentitud en móvil suele deberse a una de tres cosas: respuesta lenta del servidor (TTFB), carga pesada (imágenes/fuentes/JS/CSS) o caos de terceros (anuncios, rastreadores, widgets de chat). El truco es averiguar cuál en minutos, no en semanas.

Paso 1: Decide si es tiempo de servidor o tiempo del navegador

  • Comprueba TTFB desde una petición fría. Si TTFB es consistentemente alto (> 600–800ms) en redes móviles, arregla el servidor primero: caché, PHP-FPM, base de datos, llamadas a backends, latencia del origen.
  • Si TTFB está bien pero LCP es malo, estás en territorio front-end: imágenes, CSS que bloquea el renderizado, exceso de JS, fuentes, cambios de diseño inesperados.
  • Si TTFB a veces está bien y otras veces es terrible, sospecha de misses de caché, contención en el backend, cron o un vecino ruidoso (saturación de CPU/I/O).

Paso 2: Confirma que la caché realmente está funcionando

  • Si crees que tienes caché pero cada petición llega a PHP, no tienes caché. Tienes esperanza.
  • Busca cabeceras de caché, estado de caché en upstream y logs del servidor que muestren respuestas rápidas/estáticas.

Paso 3: Encuentra los bytes más grandes en la ruta crítica

  • En móvil, las imágenes y el JS suelen ser los habituales delincuentes.
  • Comprueba el elemento LCP: si es una imagen principal, arréglala antes de discutir sobre índices de base de datos.
  • Elimina scripts de terceros que bloqueen el hilo principal o generen tareas largas. La mayoría de scripts “imprescindibles para marketing” son opcionales una vez que demuestras el coste en rendimiento.

Paso 4: Valida con una métrica, y luego avanza

Elige una métrica de éxito por iteración: TTFB, LCP, INP (latencia de interacción) o bytes totales transferidos. No intentes arreglar todo a la vez; desplegarás un montón de cambios y no aprenderás nada.

Por qué “lento” en móvil es diferente (y por qué debería importarte)

El móvil no es solo un “escritorio más pequeño”. Es un motor físico distinto.

  • La CPU es más débil, y los navegadores limitan agresivamente para ahorrar batería.
  • La latencia de la red domina. Incluso cuando el ancho de banda parece decente, los viajes de ida y vuelta son caros. Cada consulta DNS, handshake TLS y redirección duele.
  • La presión de memoria es real. Grandes paquetes de JavaScript y temas pesados pueden forzar al navegador a hacer GC (recolección de basura) en los peores momentos.
  • La entrada es táctil. Puedes “salvarte” con un render lento en escritorio; no puedes permitirte taps con retardo.

También: no estás optimizando para un laboratorio. Estás optimizando para el usuario medio desordenado con 12 pestañas abiertas, un enlace de radio inestable y un teléfono que ha visto mejores días.

Una cita para pegar en un post-it: idea parafraseada de John Allspaw: “La fiabilidad viene de cómo se comportan los sistemas en condiciones reales, no de cómo se comportan en pruebas ideales.”

Broma #1: El trabajo de rendimiento es como hacer dieta: nadie quiere contar calorías, pero todos quieren las fotos de la playa.

Datos interesantes y contexto histórico

Algo de contexto te ayuda a dejar de repetir los grandes éxitos de la industria.

  1. WordPress se lanzó en 2003 como un fork de b2/cafelog, mucho antes de que “mobile-first” fuera algo. Muchos patrones por defecto nacieron en una era web de escritorio.
  2. HTTP/2 (estandarizado en 2015) redujo el dolor de muchas peticiones pequeñas, pero no hizo que JavaScript sin límites o imágenes gigantes fueran mágicamente baratas.
  3. Google introdujo Core Web Vitals en 2020 para cuantificar la experiencia del usuario, y eso empujó el rendimiento de “agradable” a “línea de presupuesto”.
  4. JPEG existe desde principios de los 90; formatos modernos como WebP (2010) y AVIF (ola de adopción alrededor de 2019) reducen significativamente los bytes de las fotos en móvil.
  5. El tráfico móvil superó al de escritorio hace años en muchos verticales; “somos principalmente escritorio” suele basarse en una vista analítica desactualizada o en un sesgo interno.
  6. Los CDN comenzaron como aceleradores de activos estáticos pero evolucionaron a plataformas de borde completas con caché, WAF, redimensionado de imágenes y gestión de bots—útiles para WordPress, pero fáciles de configurar mal.
  7. El patrón admin-ajax.php de WordPress se convirtió en un punto común de dolor de rendimiento a medida que temas/plugins lo usaron para funciones “en vivo”; muchos sitios se auto-DDoSearon sin querer.
  8. Lazy-loading pasó de un hack JS a nativo con loading="lazy", pero aún puede usarse mal y retrasar el LCP si cargas perezosamente la imagen principal.
  9. PHP 7 (2015) fue un salto de rendimiento; PHP 8+ añade mejoras y JIT (en su mayoría irrelevante para WordPress típico), pero las grandes ganancias vienen del opcode caching y evitar trabajo innecesario.

El único modelo mental que necesitas: TTFB vs renderizado

Las quejas sobre lentitud en móvil suelen reducirse a: “toqué y me miró fijamente”. Esa mirada es o el servidor que no envía bytes (TTFB), o el navegador que se atraganta con lo que recibió (renderizado/CPU).

Cubo A: TTFB alto (el servidor es el cuello de botella)

Causas típicas:

  • No hay caché de página (o está evitado para móvil/usuarios logueados/cadenas de consulta).
  • Ejecutación PHP lenta por plugins pesados, llamadas remotas o misses de object-cache.
  • Latencia de la base de datos (consultas malas, índices faltantes, MySQL sobrecargado, almacenamiento lento).
  • Origen lejos de los usuarios y sin caché CDN.
  • Limitación de tasa o desafíos WAF que añaden latencia a clientes móviles.

Cubo B: TTFB bajo pero pintura lenta (el front-end es el cuello de botella)

Causas típicas:

  • Imagen LCP demasiado grande, sin optimizar, servida sin tamaños responsivos.
  • CSS que bloquea el renderizado y retrasos en la carga de fuentes.
  • Bundles de JavaScript demasiado grandes, demasiados scripts de terceros, tareas largas.
  • Demasiados nodos DOM por constructores de páginas y temas “hazlo todo”.

Cubo C: Depende (intermitente)

Si el problema viene y va, asume una tormenta de misses de caché, trabajo periódico de cron, picos de tráfico o contención de recursos. Los problemas de rendimiento intermitentes rara vez se arreglan con “minificar JS” y casi siempre se arreglan eliminando la variabilidad: caché, capacidad y límites.

Tareas prácticas: comandos, salidas, decisiones (12+)

Estas son tareas de grado de producción. Cada una incluye un comando, lo que observas y qué decides después. Ejecútalas en tu host WordPress o en un clon de staging. Si no puedes ejecutar comandos (hosting gestionado), pide a tu proveedor que ejecute equivalentes—o muévete a un lugar donde puedas observar tu propio sistema.

Task 1: Measure TTFB and total time from the origin

cr0x@server:~$ curl -s -o /dev/null -w "dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total} size:%{size_download}\n" https://example.com/
dns:0.012 connect:0.034 tls:0.081 ttfb:0.742 total:1.214 size:185432

Qué significa: ttfb es el tiempo hasta el primer byte; total es la finalización de la petición; size son los bytes devueltos.

Decisión: Si TTFB es > ~0.6–0.8s de forma consistente, deja de discutir sobre formatos de imagen y arregla caché del servidor y latencia de backend primero.

Task 2: Compare cache hit vs miss using headers

cr0x@server:~$ curl -sI https://example.com/ | egrep -i "cache|age|cf-cache-status|x-cache|x-fastcgi-cache|server|vary"
server: nginx
vary: Accept-Encoding
cache-control: max-age=0, no-cache, no-store, must-revalidate
x-cache: MISS

Qué significa: Esta respuesta no es amigable con caché y muestra un miss (o no hay caché).

Decisión: Para páginas públicas, por lo general quieres cabeceras cacheables y un estado visible de caché HIT en el edge y/o en el origen. Si no puedes obtener HITs, el móvil sufrirá primero.

Task 3: Confirm whether WordPress is generating the page (PHP hit) or serving static HTML

cr0x@server:~$ tail -n 5 /var/log/nginx/access.log
203.0.113.10 - - [27/Dec/2025:10:11:22 +0000] "GET / HTTP/2.0" 200 185432 "-" "Mozilla/5.0" "upstream_response_time=0.690 request_time=1.201"
203.0.113.11 - - [27/Dec/2025:10:11:23 +0000] "GET /wp-content/uploads/2025/12/hero.jpg HTTP/2.0" 200 742193 "-" "Mozilla/5.0" "-"

Qué significa: La petición de la página principal tiene un tiempo de respuesta upstream (~0.69s) que probablemente significa que fue a PHP-FPM. La imagen se sirve directamente (sin upstream).

Decisión: Si tus páginas públicas muestran tiempos upstream regularmente, implementa caché de página completa (plugin de caché, Nginx fastcgi_cache, Varnish o caché HTML en CDN).

Task 4: Check PHP-FPM saturation (slow requests and queueing)

cr0x@server:~$ sudo ss -lntp | grep php-fpm
LISTEN 0      511          127.0.0.1:9000      0.0.0.0:*    users:(("php-fpm8.2",pid=1123,fd=8))

Qué significa: PHP-FPM está escuchando. Eso no es suficiente; necesitas saber si los workers están ocupados.

Decisión: Activa la página de estado de PHP-FPM e inspecciona active/idle/queue. Si ves cola, estás limitado por CPU o estás infra-provisionado en workers—la caché y la reducción de trabajo superan a “más workers” la mayoría de las veces.

Task 5: Verify PHP opcache is enabled and healthy

cr0x@server:~$ php -i | egrep -i "opcache.enable|opcache.memory_consumption|opcache.interned_strings_buffer|opcache.max_accelerated_files"
opcache.enable => On => On
opcache.memory_consumption => 128 => 128
opcache.interned_strings_buffer => 16 => 16
opcache.max_accelerated_files => 10000 => 10000

Qué significa: El opcode cache está activado, con ajustes moderados.

Decisión: Si opcache está apagado, enciéndelo. Si la memoria es demasiado pequeña, harás churn y perderás el beneficio—auméntala. Esto es “rendimiento aburrido”, que es el mejor tipo.

Task 6: Identify slow MySQL queries

cr0x@server:~$ sudo tail -n 20 /var/log/mysql/mysql-slow.log
# Query_time: 2.314  Lock_time: 0.000 Rows_sent: 10  Rows_examined: 512340
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
LEFT JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE wp_posts.post_type = 'product' AND wp_posts.post_status = 'publish'
ORDER BY wp_posts.post_date DESC LIMIT 0, 10;

Qué significa: Una consulta tardó 2.3 segundos y examinó medio millón de filas. El móvil siente eso como “toqué… esperé… nada”.

Decisión: Elimina/reemplaza el patrón de plugin que causa esto, añade los índices correctos (con cuidado) y confía en la caché de página para no ejecutarla por usuario.

Task 7: See if the database is I/O bound

cr0x@server:~$ iostat -xz 1 5
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.22    0.00    3.10   24.88    0.00   53.80

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         220.0   180.0  8200.0  5400.0  18.40    3.20  92.00

Qué significa: La utilización del disco es alta, con tiempo de espera no trivial; el iowait está elevado. El almacenamiento está caliente.

Decisión: Si MySQL está en la misma máquina y ves esto, necesitas menos lecturas (caché), mejores índices, almacenamiento más rápido o separación de roles. “Agregar más workers PHP” no solucionará un disco saturado.

Task 8: Check CPU and memory pressure during a slow period

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  1      0  31200  84200 612000    0    0   820   610  420  980 25  6 50 19  0
 3  2      0  29800  83500 605000    0    0  1120   900  470 1100 31  7 41 21  0

Qué significa: La cola de ejecución está arriba, hay procesos bloqueados y el iowait es significativo (wa ~ 20%). No hay actividad de swap, así que no es swapping.

Decisión: Esto apoya un cuello de botella de I/O o de base de datos. Prioriza caché y ajuste de base de datos/almacenamiento sobre cambios de front-end.

Task 9: Count third-party domains (latency tax on mobile)

cr0x@server:~$ curl -s https://example.com/ | grep -Eo 'https?://[^"]+' | awk -F/ '{print $3}' | sort -u | head
cdn.example.net
fonts.googleapis.com
fonts.gstatic.com
stats.vendor-a.com
tagmanager.vendor-b.com

Qué significa: Cada dominio de terceros añade coste DNS+TLS+petición y puede bloquear el renderizado si se usa mal.

Decisión: Si tienes más de un puñado de terceros en la ruta crítica, empieza a eliminar o diferir. En móvil, “pero es asíncrono” no es garantía; el trabajo en el hilo principal sigue ocurriendo.

Task 10: Find the heaviest images and whether you’re serving modern formats

cr0x@server:~$ find /var/www/html/wp-content/uploads -type f -name "*.jpg" -o -name "*.png" | xargs -r du -h | sort -hr | head
5.8M	/var/www/html/wp-content/uploads/2025/12/hero-homepage.jpg
4.1M	/var/www/html/wp-content/uploads/2025/12/header.png
3.9M	/var/www/html/wp-content/uploads/2025/11/banner-sale.jpg

Qué significa: Imágenes de varios megabytes son comunes y brutales en móvil, especialmente como elementos LCP.

Decisión: Convierte las imágenes hero a WebP/AVIF (donde se soporte), redimensiona a dimensiones realistas y asegura srcset responsivo. Si el tema lo desactiva, arregla el tema o reemplázalo.

Task 11: Confirm gzip/brotli compression for text assets

cr0x@server:~$ curl -sI -H "Accept-Encoding: br,gzip" https://example.com/wp-content/themes/site/style.css | egrep -i "content-encoding|content-length|vary"
content-encoding: br
vary: Accept-Encoding

Qué significa: Brotli está activado. Bien.

Decisión: Si no ves Content-Encoding, habilita gzip/brotli. Es de bajo riesgo y ayuda significativamente en móvil para CSS/JS/HTML.

Task 12: Check HTTP cacheability of static assets

cr0x@server:~$ curl -sI https://example.com/wp-content/uploads/2025/12/hero-homepage.jpg | egrep -i "cache-control|expires|etag|last-modified"
cache-control: public, max-age=31536000, immutable
etag: "a1b2c3d4"

Qué significa: Genial: caché de larga duración. Los activos estáticos deben ser cacheados agresivamente; usa versionado de archivos para actualizaciones.

Decisión: Si ves no-cache en imágenes/CSS/JS, corrige tus reglas de servidor/CDN. Si no, cada visita móvil paga el mismo coste de descarga otra vez.

Task 13: Locate WordPress cron pressure (surprise background work)

cr0x@server:~$ wp cron event list --due-now --path=/var/www/html
+--------------------------+---------------------+---------------------+------------+
| hook                     | next_run_gmt        | next_run_relative   | recurrence |
+--------------------------+---------------------+---------------------+------------+
| wc_scheduled_sales       | 2025-12-27 10:12:00 | 1 minute ago        | hourly     |
| action_scheduler_run_queue | 2025-12-27 10:10:00 | 3 minutes ago       | every_minute |
+--------------------------+---------------------+---------------------+------------+

Qué significa: Hay eventos programados pendientes. Si WP-Cron se ejecuta en cargas de usuario, puede provocar picos de latencia impredecibles.

Decisión: Desactiva la ejecución de WP-Cron en cargas de página y ejecuta un cron del sistema real que ejecute wp cron event run --due-now en un horario.

Task 14: Check PHP error logs for “performance by exception”

cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm.log
[27-Dec-2025 10:11:19] WARNING: [pool www] child 2245, script '/var/www/html/index.php' (request: "GET /") executing too slow (3.214 sec), logging
[27-Dec-2025 10:11:19] NOTICE: [pool www] child 2245 stopped for tracing, pid 2245

Qué significa: Tienes ejecución PHP lenta, no solo redes lentas. Algo dentro de WordPress está tardando segundos.

Decisión: Añade profiling a nivel de aplicación (aunque sea temporal), identifica el plugin/función del tema que consume tiempo y arréglalo o elimínalo. No solo “aumentes timeouts”.

Qué optimizar primero (ordenado, con opinión)

Esta es la parte a la que la gente quiere saltar. Está bien. Aquí está el orden que gana más a menudo en producción.

1) Consigue una caché de página real para usuarios anónimos

Si tu sitio sirve la misma página principal a miles de visitantes anónimos pero la genera con PHP+MySQL cada vez, estás quemando CPU y añadiendo latencia sin motivo. La caché de página (en el edge, proxy inverso o plugin de WordPress) es la palanca más grande para TTFB.

  • Caché HTML en el edge es lo mejor cuando puedes hacerlo con seguridad (purga en actualizaciones, evitar para páginas de carrito/login).
  • Nginx FastCGI cache es excelente si controlas el servidor.
  • Plugins de caché son mejor que nada, pero pueden ser frágiles e inconsistentes bajo carga.

Regla: si tus páginas públicas no obtienen HITs de caché, arregla eso antes de tocar la minificación.

2) Arregla la variabilidad de TTFB: cron, bypass de caché y contención backend

Los usuarios no se quejan por la media. Se quejan por picos. El móvil amplifica picos porque la red ya es lenta; añade jitter en el backend y obtienes “se queda colgado”.

  • Mueve WP-Cron fuera de las cargas de página.
  • Reduce reglas de bypass de caché (query strings, cookies, parámetros UTM).
  • Asegura que tu cache key sea sensata: móvil/escritorio deberían compartir HTML salvo que realmente sirvas marcado diferente.
  • Deja de hacer llamadas a APIs remotas en el camino de la petición. Cachea o mueve a trabajos en background.

3) Optimiza el elemento LCP (usualmente una imagen principal)

En móvil, la página “se siente cargada” cuando aparece el contenido principal. Eso es el LCP. Si tu LCP es un JPEG de 5MB escalado por CSS, tus Core Web Vitals parecerán una nota de rescate.

  • Redimensiona al tamaño renderizado real (más un poco para retina).
  • Sirve WebP/AVIF con fallback a JPEG.
  • No hagas lazy-load de la imagen LCP.
  • Precárgala si es necesario, pero solo la correcta (evita precargar activos de escritorio para móvil).

4) Elimina scripts de terceros hasta que el sitio se comporte

Aquí es donde se vuelve político. Los scripts de terceros son deuda técnica de rendimiento que pagas para siempre, en cada geografía, en cada dispositivo.

  • Elimina lo que puedas. Reemplaza lo que debas. Aloja localmente cuando sea razonable.
  • Diferir scripts no críticos. Asegúrate de que no bloqueen el renderizado.
  • Cuidado con la “expansión del tag manager”: un contenedor se convierte en cinco vendors, luego en veinte tags, luego en tristeza.

5) Reduce JavaScript y la complejidad del DOM (temas/constructores de páginas)

Los page builders facilitan prototipos rápidos y dificultan un renderizado rápido. Si tu dispositivo móvil pasa segundos ejecutando scripts y calculando layouts, puedes comprimir imágenes todo el día y aun así perder.

  • Desactiva bloques/widgets/módulos no usados.
  • Deja de enviar sliders, animaciones y mega-menús a usuarios que solo quieren leer.
  • Elige un tema que respete presupuestos de rendimiento.

6) Haz que el servidor sea aburrido: PHP moderno, opcache, FPM sensato, almacenamiento rápido

Una vez que la caché y la carga están manejadas, aún quieres un origen estable:

  • PHP 8.1+ con opcache activado.
  • PHP-FPM configurado para tu CPU/RAM (no conteos enormes de procesos que causen thrashing).
  • MySQL ajustado para tu dataset, con logging de consultas lentas activado.
  • Almacenamiento SSD/NVMe, no discos giratorios compartidos que se venden como “cloud”.

Broma #2: Si “optimizaste” instalando cinco plugins de rendimiento, no optimizaste—formaste un pequeño comité argumentativo.

Tres microhistorias corporativas desde las trincheras de rendimiento

1) Incidente causado por una suposición errónea: “El móvil está cacheado por defecto”

Una empresa mediana tenía un sitio de marketing WordPress más un blog que alimentaba leads a ventas. En escritorio parecía todo bien, y el equipo anunció orgulloso que habían activado la caché. Sin embargo, las conversiones móviles bajaron misteriosamente y los tickets de soporte describían el sitio como “pegajoso” y “aleatoriamente lento”.

La suposición errónea fue sutil: sus reglas de caché CDN variaban por user-agent, porque alguien había añadido una “optimización móvil” años antes que servía HTML ligeramente distinto. El CDN trataba al móvil como un bucket de caché separado pero no lo almacenaba por mucho tiempo debido a un TTL conservador. Peor: un plugin de marketing puso una cookie para usuarios anónimos, y el CDN estaba configurado para omitir caché siempre que existiera cualquier cookie.

Los usuarios de escritorio tendían a llegar vía visitas directas y tenían caché calentada en sus redes corporativas; los móviles llegaban vía enlaces sociales con parámetros de tracking y eran más propensos a disparar bypass de caché. Los servidores de origen eran perfectamente capaces de servir contenido cacheado en escritorio. No eran capaces de renderizar cada página móvil dinámicamente en picos de campaña.

La solución no fue “añadir más servidores”. Fue normalizar la clave de caché: ignorar cookies inofensivas, eliminar/normalizar parámetros de tracking comunes en el edge y dejar de variar HTML por user-agent salvo que fuera absolutamente necesario. También introdujeron cabeceras explícitas de estado de caché para que cualquiera pudiera ver HIT/MISS sin adivinar.

Resultado: TTFB se estabilizó. Los informes de “aleatoriamente lento” desaparecieron porque la aleatoriedad era una política de caché, no un misterio.

2) Optimización que salió mal: “Minifiquemos y combinemos todo”

Otra organización tenía un setup WordPress + WooCommerce. Las páginas móviles eran pesadas, así que el equipo activó optimizaciones agresivas: combinar CSS, combinar JS, diferir todo, inline de “critical CSS” y una docena de opciones que sonaban productivas.

Durante una semana, los números de laboratorio mejoraron. Luego empezaron las quejas de clientes: a veces los botones no funcionaban, la validación del checkout fallaba de forma intermitente y algunas páginas de producto tenían fallos de diseño solo en ciertos dispositivos Android. Marketing lo llamó “un problema de marca”, que en corporativo significa “estamos escalando esto en voz alta”.

Lo que pasó: el plugin de optimización cambió el orden de los scripts e introdujo condiciones de carrera. Las extensiones de WooCommerce esperaban que ciertas librerías existieran en un orden específico; combinar y diferir rompió esas suposiciones. Mientras tanto, HTTP/2 significaba que combinar activos en paquetes gigantes ya no era la gran ganancia que solía ser. Cambiaron fiabilidad por velocidad teórica.

El rollback restauró la funcionalidad, pero no abandonaron el trabajo de rendimiento. Adoptaron un enfoque más estrecho: no combinar a menos que demuestres que la sobrecarga de peticiones es el cuello de botella; en su lugar, elimina scripts no usados, retrasa solo tags no interactivos y mantén sagrado el flujo de checkout—transformaciones mínimas, máxima predictibilidad.

Lección: si un ajuste de velocidad puede romper el flujo de ingresos, debe tratarse como un deploy con canary, monitorización y plan de rollback. “Es solo front-end” es cómo acabas en una llamada de conferencia.

3) Práctica aburrida pero correcta que salvó el día: observabilidad de caché y líneas base de capacidad

Una compañía global gestionaba múltiples propiedades WordPress y tenía un problema recurrente: cada gran campaña causaba “sorpresas” de rendimiento. El equipo de infraestructuras no quería heroísmos; quería previsibilidad.

Añadieron dos cosas aburridas. Primero: instrumentación explícita de caché. Cada respuesta llevaba cabeceras que mostraban estado de caché en el edge, estado en el origen y si la petición llegó a PHP. Segundo: líneas base de capacidad. Mantuvieron un tablero simple: CPU del origen, I/O de disco, profundidad de cola PHP-FPM, latencia MySQL y ratio de HIT del CDN.

Durante la siguiente campaña, vieron que el ratio de HIT caía solo para tráfico móvil. No conjeturas—datos. Resultó que una herramienta de A/B testing añadida recientemente ponía una cookie en la primera visita, lo que desviaba la caché en el edge. Ajustaron el CDN para ignorar esa cookie en páginas no personalizadas, y el ratio de HIT se recuperó en minutos.

Nada ingenioso. Ninguna nueva plataforma. Solo visibilidad y una línea base que hizo medible lo “normal”. La campaña funcionó, y el postmortem fue deliciosamente corto porque la respuesta estaba en las cabeceras.

Errores comunes: síntoma → causa raíz → solución

Estos son los patrones que veo repetidamente: los mismos síntomas, las mismas malas conjeturas, las mismas soluciones que realmente funcionan.

1) Síntoma: En móvil se siente “atascado” antes de que aparezca algo

Causa raíz: TTFB alto debido a misses de caché o renderizado dinámico en cada petición.

Solución: Implementa caché de página completa para tráfico anónimo; verifica HITs con cabeceras; normaliza query strings y cookies que provocan bypass.

2) Síntoma: La primera visita es horrible, las visitas repetidas están bien

Causa raíz: No hay CDN para assets estáticos, o TTLs cortos, o caché no inmutable; el coste de la red móvil domina la primera carga.

Solución: Cabeceras de caché de larga duración para activos estáticos, offload a CDN, habilita brotli/gzip, asegura versionado de archivos.

3) Síntoma: LCP es terrible; todo lo demás carga eventualmente

Causa raíz: Imagen hero demasiado grande o retardada por lazy-load o CSS/fuentes que bloquean el renderizado.

Solución: Optimiza la imagen LCP (redimensionar + formato moderno); no la cargues perezosamente; precarga solo la variante correcta; recorta CSS que bloquea.

4) Síntoma: Tocar menú/carrito/búsqueda se siente lento

Causa raíz: Hilo principal bloqueado por JavaScript pesado y scripts de terceros; INP pobre.

Solución: Elimina scripts no usados, difiere tags no críticos, evita DOMs enormes de page builders, audita terceros y borra agresivamente.

5) Síntoma: Rendimiento bien fuera de horas, terrible durante campañas

Causa raíz: Saturación del origen (CPU, BD, disco), colas PHP-FPM, colapso de ratio de HIT de caché.

Solución: Haz la caché resistente, añade capacidad, aísla la base de datos, reduce trabajo dinámico y monitoriza profundidad de cola e I/O. No “escales” sin reducir el coste por petición.

6) Síntoma: Móvil más lento que escritorio específicamente

Causa raíz: Variación por user-agent, marcado específico para móvil, clave de caché distinta o imágenes más grandes por reglas responsivas rotas.

Solución: Unifica HTML entre dispositivos cuando sea posible; asegura que srcset funcione; confirma que el CDN no divide innecesariamente por dispositivo.

7) Síntoma: “Instalamos un plugin de caché pero nada mejoró”

Causa raíz: La caché no está sirviendo (bypass por cookies, tráfico logueado, query strings o conflictos de plugins), o el cuello de botella son JS e imágenes de terceros y no la generación de HTML.

Solución: Verifica con cabeceras y logs; si la caché HTML está en HIT pero LCP sigue mal, pasa a optimizar el elemento LCP y limpiar JS/terceros.

8) Síntoma: El admin está lento; el front-end a veces también

Causa raíz: Inflado de la base de datos, explosion de opciones autoloaded, o plugins costosos ejecutándose en todas partes.

Solución: Audita plugins, limpia opciones autoloaded, reduce tareas en background, añade object caching solo si entiendes la expulsión y la memoria.

Listas de verificación / plan paso a paso

Aquí tienes un plan pragmático que puedes ejecutar sin convertir tu sitio en un proyecto científico.

Fase 1: Establecer línea base (mismo día)

  1. Mide TTFB y tiempo total desde tu propia ruta de red del servidor (curl Task 1) y desde al menos una ubicación externa.
  2. Registra cabeceras de caché y estado de caché (Task 2). Haz capturas de pantalla de la evidencia. No confíes en la memoria.
  3. Identifica el elemento LCP en plantillas clave (homepage, categoría, producto/artículo). Si es una imagen, anota su archivo y tamaño (Task 10).
  4. Lista dominios de terceros presentes en la página (Task 9).
  5. Comprueba la salud de recursos del origen durante tráfico típico (Tasks 7 y 8).

Fase 2: Arregla el primer cuello de botella (1–3 días)

  • Si TTFB es alto:
    • Implementa caché de página completa para usuarios anónimos (edge u origen).
    • Normaliza bypass de caché: ignora parámetros de marketing; maneja cookies con cuidado.
    • Mueve WP-Cron fuera de las peticiones (Task 13).
    • Activa opcache y confirma que funciona (Task 5).
  • Si LCP es alto pero TTFB está bien:
    • Redimensiona y recodifica la imagen LCP; asegúrate de que no esté lazy-loaded.
    • Asegura que los assets estáticos tengan TTL largos (Task 12).
    • Habilita brotli/gzip (Task 11).
  • Si la interacción es lenta:
    • Elimina o difiere scripts de terceros.
    • Quita módulos del tema y widgets del page builder no usados.
    • Evita toggles de “combinar todo” a menos que sea seguro para flujos de ingresos.

Fase 3: Estabilizar y prevenir regresiones (continuo)

  1. Añade observabilidad de caché: devuelve cabeceras de estado de caché; registra tiempo de respuesta upstream; sigue ratio de HIT.
  2. Mantén el logging de consultas lentas activado, con rotación.
  3. Adopta un presupuesto de rendimiento: tamaño máximo de imagen LCP, máximo de dominios de terceros, KB máximos de JS para plantillas móviles.
  4. Prueba checkout y flujos de login con cada cambio de rendimiento (canary y rollback).

FAQ

1) ¿Por qué mi sitio WordPress solo va lento en móvil y no en escritorio?

El escritorio oculta problemas con más CPU y a menudo menor latencia. El móvil amplifica la latencia y el trabajo en el hilo principal. Si TTFB es alto, las redes móviles lo hacen sentir peor; si el JS es pesado, las CPUs móviles lo hacen sentir peor.

2) ¿Qué debo comprobar primero: imágenes u hosting?

Comprueba TTFB primero. Si TTFB es consistentemente alto, es servidor/caché/BD/origen. Si TTFB está bien, arregla imágenes LCP y JS. No compres un servidor más grande para resolver una imagen hero de 5MB.

3) ¿Necesito un CDN para rendimiento móvil?

Si tu audiencia está geográficamente distribuida o tu origen es lento, sí. Incluso localmente, un CDN ayuda cacheando assets estáticos y a veces HTML. Pero un CDN no arreglará páginas no cacheables o reglas de caché rotas.

4) ¿Un plugin de caché es suficiente?

A veces. Pero los plugins pueden ser evitados por cookies, query strings, estados logueados y comportamiento de WooCommerce. Si puedes, prefiere caché en el edge o proxy inverso donde puedas probar HIT/MISS y controlar reglas de bypass explícitamente.

5) ¿Debería combinar archivos CSS/JS para reducir peticiones?

No por defecto. Con HTTP/2/HTTP/3, tener menos peticiones es menos crítico que tener cargas más pequeñas y menos ejecución de JS. Combinar puede salir mal al romper el orden de scripts y hacer el caching menos eficiente.

6) Mis Core Web Vitals muestran INP pobre en móvil. ¿Cuál es el primer paso?

Reduce el trabajo en el hilo principal: elimina scripts de terceros, recorta JS de temas/page builders y evita widgets UI pesados. Luego busca tareas largas específicas en herramientas de rendimiento, pero el paso “eliminar primero” suele ganar.

7) WooCommerce va lento en móvil—¿qué es diferente?

Las páginas de WooCommerce a menudo no se pueden cachear igual que un blog debido a carritos, sesiones y personalización. Debes ser estricto sobre qué es cacheable (las páginas de producto suelen serlo) y mantener checkout/carrito estable y ligeramente optimizado en lugar de transformado intensamente.

8) ¿Solo indexar la base de datos puede arreglar la lentitud en móvil?

Pueda que sí si las consultas lentas dominan el TTFB y la caché no es viable (o falta caché). Pero la mayoría de las páginas públicas de WordPress no deberían ejecutar consultas pesadas por vista. Usa caché para no necesitar consultas heroicas para tráfico anónimo.

9) ¿Por qué varían mucho mis resultados de rendimiento entre pruebas?

Calentamiento de caché, variabilidad de la red y tareas en background (cron, purgas de caché, despliegues) causan fluctuaciones. TTFB intermitente a menudo apunta a misses de caché, colas PHP-FPM o contención de I/O de base de datos.

Siguientes pasos prácticos

Haz estos pasos en orden y para cuando la métrica se mueva. El rendimiento es una cola de cuellos de botella, no un solo dragón.

  1. Mide TTFB con curl y decide: backend primero o front-end primero.
  2. Demuestra que la caché funciona con cabeceras y logs; arregla reglas de bypass que matan el móvil.
  3. Optimiza el elemento LCP (casi siempre una imagen) y asegúrate de que no esté lazy-loaded.
  4. Elimina scripts de terceros hasta que el sitio funcione bien en un teléfono de gama media.
  5. Estabiliza el origen: opcache activado, WP-Cron fuera de peticiones, logging de consultas lentas activado, vigila I/O y profundidad de cola.
  6. Fija guardarraíles: presupuesto de rendimiento, monitorización y plan de rollback para “optimizaciones”.

Si quieres un mantra: optimiza primero lo que los usuarios sienten. En móvil, suele ser la capacidad de caché y el elemento LCP. Todo lo demás son trabajos de detalle—trabajos valiosos, pero solo después de detener la hemorragia.

← Anterior
Ajuste del MTU en VPN: por qué los archivos grandes se detienen mientras la web funciona (y cómo elegir el MTU correcto)
Siguiente →
Actualizaciones sin tiempo de inactividad en Docker Compose: mito, realidad y patrones que funcionan

Deja un comentario