WordPress Hosting

Trucazo rápido para saber dónde y por qué va lento WordPress

Tu WordPress va lento, abres Query Monitor a ver qué pasa, miras las consultas y… están bien, hay pocas, son rápidas, nada raro, y sin embargo la página tarda un mundo en pintarse.

Pues bien, resulta que ahí tienes el problema delante y no lo estás viendo, porque el tiempo no siempre se va en la base de datos.

Que conste, la mayoría de las veces que algo va lento la culpable sí es la base de datos, en eso llevas razón y por eso conviene empezar por ahí con la guía para optimizar las consultas SQL.

Pero cuando las consultas están limpias y la cosa se sigue arrastrando el tiempo de carga se te está yendo en otro sitio, en PHP puro, en un plugin que hace un bucle a lo bestia, en una llamada a una API externa metida dentro de un hook, en un render que se atasca.

Y no, eso en las consultas no sale.

Para medirlo con detalle existe el comando wp profile, que reparte el tiempo de carga por fases, pero necesita SSH y línea de comandos, y en un alojamiento compartido normal ni lo tienes ni te dejan tocarlo.

Así que aquí va la versión casera, un mu-plugin que te dice, al final de cada página, cuánto tiempo se ha ido en cada tramo de la carga. Sin consola, y solo lo ves solo tú.

Midiendo los tiempos de carga de una página web por fases

La carga de WordPress no es un bloque, son fases encadenadas, primero se cargan los plugins, luego arranca el tema, después se dispara init, viene la consulta principal y al final el render de la página.

La idea es poner un cronómetro en cada uno de esos pasos para ver en cuál se atasca la cosa.

Crea un archivo, por ejemplo ayudawp-phase-timer.php, y súbelo a la carpeta /wp-content/mu-plugins/ (si no existe la creas tú, es una carpeta normal y corriente).

Los mu-plugins ya sabrás se ejecutan solos y antes que todo lo demás, que es justo lo que necesitamos para cronometrar desde el arranque.

En fin, que este es el nene:

<?php
/**
* Plugin Name: Load Phase Timer
* Plugin URI: https://ayudawp.com/
* Description: Solo para admins: muestra los momentos en los que el tiempo real se solapa con el ciclo de carga de WordPress. Herramienta de diagnóstico, eliminar tras su uso.
* Version: 1.0.0
* Author: Fernando Tellado
* Author URI: https://tellado.es
 */

// Evitar el acceso directo
defined( 'ABSPATH' ) || exit;

// Solo se ejecuta cuando se activa explícitamente, para evitar una sobrecarga en el uso habitual
// Activar definiendo AYUDAWP_PHASE_TIMER como true in wp-config.php
if ( ! defined( 'AYUDAWP_PHASE_TIMER' ) || ! AYUDAWP_PHASE_TIMER ) {
	return;
}

// Una marca temporal por cada paso del ciclo
$GLOBALS['ayudawp_phase_marks'] = array();

// Ancla al inicio de la solicitud real, definida por PHP incluso antes de que se cargue WordPress
$GLOBALS['ayudawp_phase_marks']['request_start'] = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
	? (float) $_SERVER['REQUEST_TIME_FLOAT']
	: microtime( true );

// Momento en que este mu-plugin empieza a ejecutarse
$GLOBALS['ayudawp_phase_marks']['muplugins_start'] = microtime( true );

/**
 * Registra el tiempo real de cada paso
 */
function ayudawp_mark_phase( $name ) {
	$GLOBALS['ayudawp_phase_marks'][ $name ] = microtime( true );
}

// Marca temporal de cada paso del ciclo de carga, en orden
add_action( 'muplugins_loaded',  function () { ayudawp_mark_phase( 'muplugins_loaded' ); },  0 );
add_action( 'plugins_loaded',    function () { ayudawp_mark_phase( 'plugins_loaded' ); },    0 );
add_action( 'after_setup_theme', function () { ayudawp_mark_phase( 'after_setup_theme' ); }, 0 );
add_action( 'init',              function () { ayudawp_mark_phase( 'init' ); },              0 );
add_action( 'wp_loaded',         function () { ayudawp_mark_phase( 'wp_loaded' ); },         0 );
add_action( 'template_redirect', function () { ayudawp_mark_phase( 'template_redirect' ); }, 0 );

// Al final de la solicitud muestra el informe (solo admins)
add_action( 'shutdown', 'ayudawp_print_phase_report', PHP_INT_MAX );

/**
 * Genera y muestra el desglose por fases como comentario HTML, solo para admins
 */
function ayudawp_print_phase_report() {

	// Nunca expone tiempos internos a visitantes
	if ( ! function_exists( 'current_user_can' ) || ! current_user_can( 'manage_options' ) ) {
		return;
	}

	ayudawp_mark_phase( 'shutdown' );
	$marks = $GLOBALS['ayudawp_phase_marks'];

	// Etiqueta para humanos => [ from milestone, to milestone ]. Cadena contigua
	$phases = array(
		'Antes de WordPress (arranque PHP)' => array( 'request_start', 'muplugins_start' ),
		'Carga de mu-plugins'               => array( 'muplugins_start', 'muplugins_loaded' ),
		'Carga de plugins'                  => array( 'muplugins_loaded', 'plugins_loaded' ),
		'Arranque del tema'                 => array( 'plugins_loaded', 'after_setup_theme' ),
		'Hasta init'                        => array( 'after_setup_theme', 'init' ),
		'De init a wp_loaded'               => array( 'init', 'wp_loaded' ),
		'Consulta principal'                => array( 'wp_loaded', 'template_redirect' ),
		'Render y salida'                   => array( 'template_redirect', 'shutdown' ),
	);

	$total = ( $marks['shutdown'] - $marks['request_start'] ) * 1000;

	$lines   = array();
	$lines[] = 'AyudaWP Load Phase Timer';
	$lines[] = 'URL: ' . ( isset( $_SERVER['REQUEST_URI'] ) ? esc_html( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : 'n/a' );
	$lines[] = str_repeat( '-', 52 );

	foreach ( $phases as $label => $pair ) {
		list( $from, $to ) = $pair;

		// Omite fases cuyos pasos no se hayan activado (p.ej. template_redirect en wp-admin)
		if ( ! isset( $marks[ $from ], $marks[ $to ] ) ) {
			continue;
		}

		$ms  = ( $marks[ $to ] - $marks[ $from ] ) * 1000;
		$pct = $total > 0 ? ( $ms / $total ) * 100 : 0;
		$lines[] = sprintf( '%-38s %8.1f ms  %5.1f%%', $label, $ms, $pct );
	}

	$lines[] = str_repeat( '-', 52 );
	$lines[] = sprintf( '%-38s %8.1f ms', 'TOTAL', $total );

	$output = implode( "\n", $lines );

	// Red de seguridad: no dejar que el contenido salga del comentario HTML
	$output = str_replace( '-->', '--&gt;', $output );

	echo "\n<!--\n" . $output . "\n-->\n"; // phpcs:ignore WordPress.Security.EscapeOutput
}

Para que no te consuma nada en el día a día, el cronómetro solo se enciende cuando tú quieres. Añade esta línea a tu wp-config.php mientras diagnosticas y la quitas al terminar:

// Activa el temporizador de fase de AyudaWP para medir el tiempo de carga de las páginas
define( 'AYUDAWP_PHASE_TIMER', true );

Interpretar el reparto de tiempos

Abre como administrador la página que va lenta, mira el código fuente (botón derecho, Ver código fuente de la página) y busca al final del todo un comentario de código con el reparto de tiempos, verás algo así:

<!--
AyudaWP Load Phase Timer
URL: /mi-pagina-lentorra/
----------------------------------------------------
Antes de WordPress (arranque PHP) 37.8 ms 23.9%
Carga de mu-plugins 0.1 ms 0.1%
Carga de plugins 17.9 ms 11.3%
Arranque del tema 8.4 ms 5.3%
Hasta init 3.9 ms 2.5%
De init a wp_loaded 18.0 ms 11.4%
Consulta principal 8.1 ms 5.1%
Render y salida 63.8 ms 40.4%
----------------------------------------------------
TOTAL 158.1 ms
-->

La fase con más milisegundos es donde se te está yendo el tiempo. A partir de ahí:

  • Si se concentra en Carga de plugins o en Hasta init, tienes demasiados plugins o alguno hace trabajo pesado al arrancar, así que toca revisar cuáles son y desactivar los que no aporten.
  • Si el tiempo está en Consulta principal y las consultas estaban limpias, mira el bucle principal, las WP_Query mal montadas o los relacionados, y vuelve a esa guía de consultas SQL.
  • Si el grueso está en Render y salida, el problema es de la plantilla, de shortcodes pesados o de scripts, no de la base de datos.

Ojo, esto es para direcciones de la parte visible de la web, en el escritorio (wp-admin) algunas fases no aparecen porque no llegan a dispararse, y es lo normal.

Y que quede claro, esto es un primer vistazo y no sustituye a nada. Cuando ya sepas donde se atasca la cosa y quieras al culpable concreto Query Monitor te atribuye cada consulta, cada llamada HTTP y cada hook al plugin que lo provoca. Y si tienes SSH, wp profile te da todo esto y bastante más.

Una última cosa, que luego todo son sustos, esto es una herramienta de diagnóstico y no se queda puesta. Cuando termines quita la línea del wp-config.php o borra directamente el archivo, que un cronómetro corriendo en cada carga, por ligero que sea, no pinta nada en producción.

¿Te ha salido disparada una fase que no te esperabas? Cuéntamelo aquí abajo en los comentarios, que seguro que no eres el único al que le pasa.

Compártelo en tus redes
Resúmelo con tu IA

¿De cuánta utilidad te ha parecido este contenido?

¡Haz clic en las estrellas para valorarlo!

Promedio de puntuación 5 / 5. Total de votos: 2

¡Todavía no hay votos! Sé el primero en valorar este contenido.

Ya que has encontrado útil este contenido...

¡Sígueme en las redes sociales!

¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!



Sobre el autor

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio