Pulsa ESC para cerrar

Curso WordpressCurso Wordpress

Guardar una entrada en WordPress tardaba 20 segundos: así encontré al culpable

En uno de mis WordPress, pulsar Actualizar en una entrada ya publicada empezó a tardar más de 20 segundos. No salía ningún error: solo la rueda girando hasta que por fin aparecía «Entrada actualizada». Abrir el editor, cambiar de pantalla o entrar a Ajustes iba rápido, así que no era «el servidor lento» en general.

Aquí cuento paso a paso cómo encontré la causa sin tocar nada hasta saber qué era. Todos los comandos de diagnóstico son de solo lectura: miran, pero no cambian nada. La causa resultó ser una casilla de WP-Optimize, pero el método sirve para encontrar cualquier plugin que haga lo mismo.

Antes de empezar

  • Necesitas un VPS o servidor dedicado con cPanel/WHM y acceso root. Para pegar los comandos basta con WHM → Terminal, o una conexión SSH como root.
  • Lo probé en AlmaLinux 10 con cPanel, PHP-FPM de EasyApache y el sitio detrás de Cloudflare. No hace falta WP-CLI.
  • Usa Chrome, Edge o Firefox en un computador. Las herramientas de desarrollador no existen en el móvil.
  • En los comandos, cambia tudominio.cl por tu dominio. Lee cada bloque antes de pegarlo: se comparten tal cual, sin garantía.

Paso 1. Medir en el navegador dónde se va el tiempo

Primero hay que saber si la demora está en tu servidor, en Cloudflare o en tu conexión. El navegador lo mide por ti.

  1. Entra al administrador de WordPress y abre para editar una entrada que ya esté publicada (Entradas → pasa el ratón sobre el título → Editar).
  2. Pulsa F12 (en Mac: Cmd + Opción + I). Se abre un panel con varias pestañas. Elige Red (en inglés, Network).
  3. En la barra de esa pestaña, marca Conservar registro (Preserve log). Es importante: al guardar la entrada, WordPress recarga la página, y sin esta casilla el navegador borra la lista justo cuando la necesitas.
  4. Haz un cambio mínimo en la entrada (por ejemplo, agrega un espacio al final de un párrafo) y pulsa el botón azul Actualizar:
    • Editor clásico: en la caja Publicar, a la derecha.
    • Editor de bloques: arriba a la derecha.
    • Elementor: abajo a la izquierda del panel (o arriba, según la versión).
  5. Espera a que termine de guardar. Verás muchas filas nuevas en la pestaña Red.
  6. Escribe en el cuadro Filtro el nombre de la petición que corresponde a tu editor:
    • Editor clásico: post.php (la que dice POST en la columna Método, o un estado 302).
    • Editor de bloques: wp-json (busca /wp/v2/posts/ seguido del número de la entrada).
    • Elementor: admin-ajax (la más lenta suele ser la de la acción elementor_ajax).
  7. Mira la columna Tiempo. Si la petición tarda varios segundos, esa es la culpable. Haz clic en ella.

Se abre un panel con los detalles. Revisa dos cosas:

  • En la pestaña Tiempos (Timing), la barra Esperando la respuesta del servidor (Waiting for server response). Si casi todo el tiempo está ahí, tu servidor está trabajando (o esperando algo) antes de responder.
  • En la pestaña Encabezados (Headers), dentro de Encabezados de respuesta, busca server-timing si usas Cloudflare. Separa el tiempo de Cloudflare (cfEdge) del de tu servidor (cfOrigin).

En mi caso:

POST /wp-admin/post.php   302   23,0 s
server-timing: cfEdge;dur=3,cfOrigin;dur=22910

dur está en milisegundos: Cloudflare tardó 3 ms y mi servidor casi 23 segundos. Eso descarta Cloudflare y la conexión. El estado 302 es la respuesta normal de WordPress al guardar una entrada (redirige de vuelta al editor). Si fuera un bloqueo de ModSecurity o de Cloudflare, verías un 403.

Si no usas Cloudflare no verás cfOrigin, pero la barra «Esperando la respuesta del servidor» te dice lo mismo. Y si el tiempo alto está en otra parte (por ejemplo, en «Descarga del contenido»), el problema es de conexión, no de este artículo.

Paso 2. Mirar el servidor mientras guardas

Ya sabemos que el servidor es el que tarda al guardar la entrada. Ahora toca ver qué hace durante esos 20 segundos: si está calculando, consultando la base de datos o esperando algo de la red.

Prepara dos ventanas una al lado de la otra: la Terminal de WHM (o tu SSH como root) y el editor de la entrada en el navegador, listo para pulsar Actualizar.

Pega este bloque completo en la terminal y pulsa Enter. Cambia tudominio.cl en la primera línea:

DOM=tudominio.cl
U=$(awk -F': ' -v d="$DOM" '$1==d{print $2}' /etc/userdomains)
echo "Cuenta de cPanel: $U"
for i in $(seq 1 20); do
  echo "=== $(date +%T)"
  ps -u "$U" -o pid,etime,pcpu,stat,wchan:20,cmd --no-headers | grep php | grep -v grep
  ss -tnpa 2>/dev/null | grep php | grep -vE "LISTEN|:3306"
  mysql -N -e "SELECT time,state,LEFT(info,120) FROM information_schema.processlist WHERE command<>'Sleep' AND info IS NOT NULL AND info NOT LIKE '%processlist%';"
  sleep 2
done

Qué hace: durante unos 40 segundos, cada 2 segundos, muestra tres cosas:

  • ps: los procesos PHP de la cuenta, cuánto llevan vivos (etime), cuánta CPU usan (pcpu) y en qué están esperando si están dormidos (wchan).
  • ss: las conexiones de red abiertas por esos procesos, sin contar MySQL.
  • mysql: las consultas que la base de datos está ejecutando en ese momento.

Apenas veas la primera línea === hora, pulsa Actualizar en el navegador. El bloque termina solo. Si quieres cortarlo antes, pulsa Ctrl + C.

Si la línea «Cuenta de cPanel» sale vacía, el dominio no está escrito igual que en cPanel. Revisa con grep tudominio /etc/userdomains.

Lo que vi, resumido:

=== 05:01:12
245518  00:02  77.0  S  poll_schedule_timeou  php-fpm: pool tudominio_cl
ESTAB   IP-DEL-SERVIDOR:53654   104.21.x.x:443   users:(("php-fpm",pid=245518,fd=9))
=== 05:01:14
245518  00:05  46.0  S  hrtimer_nanosleep     php-fpm: pool tudominio_cl
245530  00:02  70.6  S  skb_wait_for_more_pa  php-fpm: pool tudominio_cl
...
=== 05:01:18
ESTAB   IP-DEL-SERVIDOR:36706   104.21.x.x:443   users:(("php-fpm",pid=245518,fd=9))

Cómo leerlo:

  • MySQL no aparecía (o solo con consultas de 0 segundos). La base de datos no era el problema.
  • El proceso 245518 (el que guardaba la entrada) abría una conexión HTTPS tras otra hacia una IP 104.21.x.x por el puerto 443. Fíjate en que el número después de IP-DEL-SERVIDOR: cambia: cada uno es una conexión nueva.
  • Entre conexión y conexión, ese proceso dormía en hrtimer_nanosleep, que es una pausa programada en el código. Era un bucle de «pedir una página, esperar, pedir otra».
  • Mientras tanto aparecían procesos PHP nuevos (245530 y otros) en la misma cuenta.

La columna wchan dice en qué está esperando un proceso dormido. poll_schedule_timeout suele ser una espera de red y hrtimer_nanosleep un sleep() en el código. Si en cambio ves la CPU (pcpu) cerca de 100 todo el rato, el problema sería un cálculo pesado, no una espera.

¿A quién se conectaba? Compara esa IP con la de tu propio dominio:

getent ahostsv4 tudominio.cl | awk '{print $1}' | sort -u

Me devolvió las mismas IP 104.21.x.x y 172.67.x.x (son de Cloudflare). O sea: al guardar la entrada, WordPress se estaba visitando a sí mismo. Esas visitas entraban de nuevo al servidor y eran los procesos PHP nuevos que aparecían.

Paso 3. Ver qué páginas se pedía a sí mismo

Apache anota cada visita en el log de acceso del dominio, con la IP que la hizo y su user agent (el nombre del programa que visita). Las visitas que hace el propio servidor llevan su IP, y el user agent suele decir qué plugin las hizo.

  1. Averigua la IP de tu servidor (la primera que aparezca):
    hostname -I
  2. Vuelve al navegador y pulsa Actualizar en la entrada otra vez. Espera a que termine de guardar.
  3. Pega esto en la terminal, cambiando el dominio y poniendo tu IP en lugar de IP-DEL-SERVIDOR (con las barras invertidas antes de cada punto, por ejemplo ^203\.0\.113\.10 ):
    LOG=/etc/apache2/logs/domlogs/tudominio.cl-ssl_log
    tail -n 4000 "$LOG" | grep -E "^IP-DEL-SERVIDOR |WordPress/" | tail -n 40

Si dice que el archivo no existe, busca el nombre correcto con ls /etc/apache2/logs/domlogs/. En algunos servidores está dentro de una carpeta con el nombre de la cuenta.

Ahí apareció el culpable con nombre y apellido:

IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:12 +0000] "GET /categorias/mi-categoria/ HTTP/1.1" 200 2427 "-" "WP-Optimize/4.7.0 WordPress/7.1.3 (Desktop; ...)"
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:13 +0000] "GET /categorias/mi-categoria/ HTTP/1.1" 200 2437 "-" "WP-Optimize/4.7.0 WordPress/7.1.3 (Mobile; ...)"
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:14 +0000] "GET / HTTP/1.1" 200 2427 "-" "WP-Optimize/4.7.0 WordPress/7.1.3 (Desktop; ...)"
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:15 +0000] "GET / HTTP/1.1" 200 2437 "-" "WP-Optimize/4.7.0 WordPress/7.1.3 (Mobile; ...)"
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:24 +0000] "GET /2026/ HTTP/1.1" 200 ...
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:27 +0000] "GET /2026/10/ HTTP/1.1" 200 ...
IP-DEL-SERVIDOR - - [26/Oct/2026:05:01:31 +0000] "GET /author/mi-usuario/ HTTP/1.1" 200 ...
... y dos líneas más por cada entrada de la categoría

Cada línea es una página: la hora, la dirección pedida, el resultado (200 = bien) y, al final, quién la pidió: WP-Optimize, primero como escritorio (Desktop) y luego como móvil (Mobile).

Es decir, cada vez que guardaba una entrada, WP-Optimize regeneraba la caché de todas las páginas relacionadas con la entrada: la portada, la categoría, los archivos por año y mes, la página del autor y las demás entradas de la categoría. Lo hacía dos veces por página, y una por segundo. Eran unas 20 visitas, y el botón Actualizar no terminaba hasta que acababan todas. Por eso, mientras más entradas tenía la categoría, más tardaba.

La causa: precargar el contenido purgado

Cuando actualizas una entrada, WP-Optimize purga (borra) la caché de las páginas donde aparece. Eso es correcto: si no, los visitantes verían la versión vieja. El problema es una opción que, además, las precarga (las vuelve a generar) en ese mismo momento y te hace esperar a que termine.

Puedes comprobar si la tienes activa leyendo el archivo de configuración de WP-Optimize. Cambia USUARIO por la cuenta de cPanel que mostró el paso 2:

grep -ohE '"[a-z_]*(preload|mobile|purge)[a-z_]*":[^,]*' /home/USUARIO/public_html/wp-content/wpo-cache/config/config-*.php
"enable_schedule_preload":"0"
"enable_mobile_caching":"1"
"auto_preload_purged_contents":"1"   <-- esta es la que causa la demora

"1" significa activada y "0" desactivada.

La solución

  1. Antes de cambiar nada, haz una captura de pantalla de cómo están las opciones, para poder dejarlas igual si hace falta.
  2. En el menú lateral de WordPress, entra a WP-Optimize. Arriba a la derecha, haz clic en el icono Cache.
  3. Quédate en la primera pestaña, Page cache. Ojo: la opción no está en la pestaña Preload, aunque el nombre lo sugiera. Esa pestaña solo tiene la precarga manual y la programada.
  4. Baja hasta encontrar la casilla «Automatically preload content that is purged» (si tienes el plugin en español, algo como «Precargar automáticamente el contenido purgado») y desmárcala.
  5. Baja hasta el final de la pestaña y pulsa Save changes.

Hazlo desde el panel y no editando el archivo a mano, para que WP-Optimize regenere bien su configuración. Si quieres volver atrás, basta con marcar la casilla de nuevo.

Comprobar que funcionó

  • Repite el paso 1: guarda la entrada con la pestaña Red abierta y mira el tiempo de la petición. Debería bajar de forma drástica.
  • Opcional: repite el comando del archivo de configuración. Ahora debería decir "auto_preload_purged_contents":"0".

En mi caso, el guardado pasó de unos 22 segundos a ser prácticamente inmediato. La caché sigue funcionando igual para los visitantes. Lo único que cambia es que, después de actualizar una entrada, la primera visita a cada página afectada tarda un poco más, porque es esa visita la que vuelve a generar la caché. Las siguientes ya la reciben lista.

Si tu tema es responsive (casi todos lo son), revisa también «Generate separate files for mobile devices», en la misma pestaña Page cache. Con esa opción activa, WP-Optimize genera y guarda cada página dos veces. Solo hace falta si tu sitio muestra un HTML distinto en móviles, por ejemplo con un plugin que cambia el tema en el móvil.

Cuándo sospechar de esto

  • Guardar o actualizar tarda mucho, pero abrir el editor y navegar por el administrador va rápido.
  • Tarda más en las entradas de categorías con muchas entradas.
  • Usas un plugin de caché que «precarga» o «calienta» la caché. WP-Optimize, WP Rocket, LiteSpeed Cache y W3 Total Cache tienen opciones parecidas.

El método del paso 3 sirve para todos: si en el log aparece tu propio servidor pidiendo páginas justo después de guardar una entrada, el user agent te dice qué plugin es.