
Si tienes un tema de WordPress activo desde hace años, hay muchas posibilidades de que esté lastrando tus puntuaciones en Core Web Vitals sin que te hayas dado cuenta. Los trucos que circulaban en 2012 —combinar JS a mano, minificar CSS con Notepad++, añadir un favicon con un hook— ya no son los que marcan la diferencia. Hoy Google mide LCP, INP y CLS, y tu tema es responsable directo de los tres.
Esta guía recoge 10 técnicas reales que puedes aplicar en tu functions.php, en el editor de bloques o directamente en la configuración del tema para que tu WordPress cargue más rápido, ocupe menos y pase los umbrales de rendimiento que Google considera aceptables. No hay magia: son cambios concretos, con código cuando hace falta.
Si buscas una visión más amplia de rendimiento (caché, base de datos, servidor), consulta nuestra guía práctica de rendimiento WordPress. Lo que encontrarás aquí es específico del tema: lo que controlas desde el código y la configuración del propio theme.
El problema más frecuente que bloquea el render en temas de WordPress no es el CSS: es el JavaScript. Cada vez que un script se carga sin defer o async, el navegador para de construir el DOM hasta que termina de descargarlo y ejecutarlo. El resultado visible es una métrica LCP disparada y un INP que da pena.
La solución limpia en WordPress es filtrar los scripts en el hook script_loader_tag. Añade esto en tu functions.php o en un plugin de funciones del hijo:
add_filter( 'script_loader_tag', 'wpdirecto_defer_scripts', 10, 3 );
function wpdirecto_defer_scripts( $tag, $handle, $src ) {
// Lista de handles que NO deben diferirse
$no_defer = [ 'jquery', 'wp-polyfill' ];
if ( in_array( $handle, $no_defer ) ) {
return $tag;
}
// Añade defer a todos los demás
return str_replace( ' src', ' defer src', $tag );
}Lenguaje del código: PHP (php)
Importante: jQuery no se puede diferir si otros scripts dependen de él en el mismo ciclo de render. Revisa las dependencias con Query Monitor antes de incluirlo en la lista de defer.
Las fuentes web son un clásico bloqueante que muchos temas siguen cargando mal. Cuando el navegador descarga una tipografía antes de pintar el texto, el usuario ve una pantalla en blanco o un parpadeo de tipografía invisible (FOIT). La propiedad CSS font-display: swap indica al navegador que use primero la fuente del sistema y sustituya cuando la tuya cargue.
Si tu tema carga fuentes de Google Fonts con wp_enqueue_style, puedes forzar el parámetro display=swap en la URL:
// En functions.php del tema hijo
add_filter( 'style_loader_src', 'wpdirecto_fonts_display_swap', 10, 2 );
function wpdirecto_fonts_display_swap( $src, $handle ) {
if ( strpos( $src, 'fonts.googleapis.com' ) !== false ) {
if ( strpos( $src, 'display=' ) === false ) {
$src = add_query_arg( 'display', 'swap', $src );
}
}
return $src;
}Lenguaje del código: PHP (php)
Si usas un tema de bloques (FSE), revisa si las fuentes están definidas en theme.json. Desde WordPress 6.4 puedes incluir la propiedad fontDisplay directamente en el JSON del tema.
WordPress lleva activando loading="lazy" por defecto desde la versión 5.5 en imágenes insertadas con el editor. El problema es que los temas que construyen sus propias llamadas a get_the_post_thumbnail() o imágenes en widgets no siempre respetan ese atributo.
Verifica que todas las imágenes fuera del fold lo tengan. Para imágenes dinámicas en el tema, añádelo explícitamente:
// Forzar loading=lazy en todas las imágenes del contenido
add_filter( 'the_content', 'wpdirecto_add_lazy_loading' );
function wpdirecto_add_lazy_loading( $content ) {
return str_replace( '<img ', '<img loading="lazy" ', $content );
}
// Para la imagen destacada en bucles
$args = array(
'loading' => 'lazy',
'fetchpriority' => 'low',
);
echo get_the_post_thumbnail( get_the_ID(), 'medium_large', $args );Lenguaje del código: PHP (php)
Excepción importante: la imagen above-the-fold (el hero o la thumbnail en la entrada individual) no debe tener loading="lazy". Esa imagen necesita fetchpriority="high" para mejorar el LCP.
El Largest Contentful Paint (LCP) mide cuánto tarda en pintarse el elemento principal de la página —normalmente la imagen del hero o la thumbnail grande en posts. Si esa imagen tarda en descargarse, el LCP falla. La solución es un <link rel="preload"> en el <head> que le dice al navegador que empiece a descargarla antes de que el HTML la encuentre en el body.
add_action( 'wp_head', 'wpdirecto_preload_hero_image', 1 );
function wpdirecto_preload_hero_image() {
if ( is_singular() && has_post_thumbnail() ) {
$img = wp_get_attachment_image_src( get_post_thumbnail_id(), 'large' );
if ( $img ) {
echo '<link rel="preload" as="image" href="' . esc_url( $img[0] ) . '" fetchpriority="high">' . "n";
}
}
}Lenguaje del código: PHP (php)
En temas de bloques, puedes lograr el mismo efecto añadiendo fetchpriority="high" en el bloque de imagen del template del single en el editor de sitio.
Muchos temas cargan hojas de estilo enormes con estilos para todas sus páginas, aunque la mayoría no se usen en la página concreta que el usuario está visitando. Eso es CSS render-blocking: el navegador espera a que cargue todo antes de pintar nada.
La aproximación moderna es cargar el CSS crítico (above-the-fold) inline en el <head> y diferir el resto. Para identificar qué CSS es realmente crítico, usa herramientas como PageSpeed Insights o el panel de Coverage en DevTools de Chrome.
Una táctica efectiva desde functions.php es desregistrar los estilos del tema que no se necesitan en páginas concretas. Por ejemplo, si tu tema carga un stylesheet de WooCommerce en todas las páginas aunque solo tengas tienda en /tienda:
add_action( 'wp_enqueue_scripts', 'wpdirecto_dequeue_unused_css', 99 );
function wpdirecto_dequeue_unused_css() {
if ( ! is_woocommerce() && ! is_cart() && ! is_checkout() ) {
wp_dequeue_style( 'woocommerce-general' );
wp_dequeue_style( 'woocommerce-layout' );
wp_dequeue_style( 'woocommerce-smallscreen' );
}
}Lenguaje del código: JavaScript (javascript)
Complementa esto con el truco del .htaccess para añadir cabeceras de caché a los assets estáticos. Tienes todos los detalles en nuestra guía de .htaccess en WordPress: 15 trucos útiles y cabeceras de seguridad.
WordPress genera automáticamente versiones WebP de las imágenes subidas desde la versión 6.1. Pero si tu tema tiene imágenes hardcodeadas en PHP o HTML, esas siguen siendo JPEG o PNG. La diferencia de peso entre un JPEG y su equivalente WebP es real: en torno a un 25-35% menos en promedio.
Para imágenes dinámicas del tema, usa wp_get_attachment_image() que desde WordPress 6.1 genera automáticamente el atributo srcset con las versiones WebP disponibles. Para imágenes estáticas del tema (logos, iconos de fondo), convîrtelas manualmente y sirve el formato moderno con un elemento <picture>:
<picture>
<source srcset="<?php echo get_template_directory_uri(); ?>/images/hero.avif" type="image/avif">
<source srcset="<?php echo get_template_directory_uri(); ?>/images/hero.webp" type="image/webp">
<img src="<?php echo get_template_directory_uri(); ?>/images/hero.jpg" alt="Hero image" loading="eager" fetchpriority="high" width="1200" height="600">
</picture>Lenguaje del código: HTML, XML (xml)
Incluir siempre los atributos width y height en las imágenes del tema es obligatorio: evita el CLS (Cumulative Layout Shift) que Google penaliza directamente en Core Web Vitals.
Google recomienda que el DOM no supere los 1.500 nodos. Muchos temas comerciales —sobre todo los pagebuilders clásicos como Divi o Avada— generan 4.000-6.000 nodos en una página normal. Eso ralentiza tanto el primer paint como cualquier interacción posterior (INP).
Desde el tema, puedes reducir el DOM de varias formas prácticas:
<div class="wrapper"><div class="inner"><div class="content">, usa directamente <main> o <article>.// Desactivar funciones del tema que no usas desde functions.php del hijo
add_action( 'after_setup_theme', 'wpdirecto_remove_theme_features', 11 );
function wpdirecto_remove_theme_features() {
remove_theme_support( 'custom-background' );
remove_theme_support( 'custom-header' );
remove_theme_support( 'widgets-block-editor' ); // si no usas widgets de bloques
}Lenguaje del código: JavaScript (javascript)
Hay una trampa habitual en WordPress: plugins que tu tema «recomienda» al instalarse y que siguen activos aunque nunca los hayas configurado. Cada plugin activo añade tiempo de carga al wp-load.php, aunque no haga nada visible.
La herramienta correcta para diagnosticar esto es Query Monitor. Muestra exactamente qué plugins añaden queries SQL, hooks y tiempo de ejecución en cada carga de página. Una vez identifiques los culpables, desáctivalos.
Otra fuente frecuente de sobrecarga son los plugins de redes sociales con sus propios CSS/JS: AddToAny, ShareThis, etc. Los botones de compartir pueden implementarse hoy con HTML puro sin JavaScript (Web Share API para móvil, links directos para escritorio):
<!-- Botones de compartir sin JavaScript -->
<div class="share-buttons">
<a href="https://twitter.com/intent/tweet?url=<?php echo urlencode(get_permalink()); ?>&text=<?php echo urlencode(get_the_title()); ?>" rel="noopener noreferrer" target="_blank">X / Twitter</a>
<a href="https://www.linkedin.com/sharing/share-offsite/?url=<?php echo urlencode(get_permalink()); ?>" rel="noopener noreferrer" target="_blank">LinkedIn</a>
<a href="https://api.whatsapp.com/send?text=<?php echo urlencode(get_the_title() . ' ' . get_permalink()); ?>" rel="noopener noreferrer" target="_blank">WhatsApp</a>
</div>Lenguaje del código: HTML, XML (xml)
La caché de página (page cache) guarda el HTML generado para servirlo directamente sin ejecutar PHP ni consultar la base de datos. La caché de objetos (object cache) guarda los resultados de queries SQL para reutilizarlos dentro de la misma carga de página. Ambas son independientes del tema, pero el tema puede sacarles partido —o arruinarlas.
Los temas que usan rand() en sus queries WP_Query (para mostrar posts aleatorios, por ejemplo) rompen la caché de página porque cada petición genera HTML diferente. Evita esto desde el tema:
// MAL: rompe la caché de página
$args = array(
'post_type' => 'post',
'orderby' => 'rand', // esto genera HTML diferente cada vez
'posts_per_page' => 3,
);
// BIEN: usa un orden determinista
$args = array(
'post_type' => 'post',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => 3,
'cache_results' => true, // activa caché de objetos explícitamente
'update_post_meta_cache' => true,
);Lenguaje del código: PHP (php)
Para la caché de página completa, las opciones más sólidas son LiteSpeed Cache (si tu hosting usa LiteSpeed), WP Rocket (premium, la más completa) o W3 Total Cache con Redis para object cache. Tienes la comparativa detallada en nuestra guía del mejor plugin de caché para WordPress.
WordPress añade en el <head> una serie de etiquetas que son innecesarias para la mayoría de sitios: el generador de versión, el link de XMLRPC, el manifest de Windows Live Writer, los enlaces de feed RSS en la cabecera y los prefetch de emojis. Cada uno es una petición HTTP extra o información que expone la versión de WordPress.
// Limpiar el head de WordPress en 2026
add_action( 'init', 'wpdirecto_clean_head' );
function wpdirecto_clean_head() {
// Ocultar versión de WordPress
remove_action( 'wp_head', 'wp_generator' );
// Quitar RSD link (XML-RPC)
remove_action( 'wp_head', 'rsd_link' );
// Quitar Windows Live Writer manifest
remove_action( 'wp_head', 'wlwmanifest_link' );
// Quitar links de feed del head (si no usas suscriptores RSS)
remove_action( 'wp_head', 'feed_links', 2 );
remove_action( 'wp_head', 'feed_links_extra', 3 );
// Quitar preload de emojis (ahorra 1 petición HTTP)
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
// Quitar shortlinks
remove_action( 'wp_head', 'wp_shortlink_wp_head' );
// Quitar REST API link del head
remove_action( 'wp_head', 'rest_output_link_wp_head' );
}Lenguaje del código: JavaScript (javascript)
Esta limpieza del head es especialmente útil si combinada con las cabeceras de seguridad que puedes configurar en el .htaccess. La guía de .htaccess en WordPress detalla cómo añadir X-Content-Type-Options y otras cabeceras de seguridad al mismo tiempo.
Si estás pensando en cambiar de tema, la decisión más importante que tomarás es si migrar a un tema de bloques (FSE) o quedarte con un tema clásico. La diferencia de rendimiento es real:
theme.json.El rendimiento de un tema no depende solo de lo que hace, sino de lo que no hace. Los trucos de 2012 tenían sentido en su momento; hoy la clave está en cargar menos, diferir más y medir con las métricas que Google realmente usa para rankear.
Si después de aplicar estos cambios en el tema sigues con malas puntuaciones, el siguiente paso es revisar el hosting y la configuración del servidor. En esa dirección te ayuda nuestra guía de rendimiento WordPress y la comparativa de plugins de caché.
En general, los temas de bloques generan DOMs más pequeños y cargan menos recursos por defecto, lo que mejora las métricas de INP y LCP. Los temas clásicos bien configurados (como Astra o GeneratePress) pueden alcanzar resultados similares, pero requieren más trabajo manual de optimización. Si empiezas desde cero, un tema de bloques es la elección con mejor base de partida.
Técnicamente sí, pero no es recomendable. Si modificas el functions.php del tema padre, perderás todos los cambios cuando actualices el tema. Siempre trabaja con un tema hijo o, mejor aún, con un plugin de funciones del sitio que centralice todo el código personalizado.
Usa PageSpeed Insights para ver las métricas Core Web Vitals reales (campo) y de laboratorio. Complementa con Query Monitor en el dashboard de WordPress para identificar queries lentas, scripts bloqueantes y tiempo de carga de cada componente del tema. Google Search Console también muestra los datos de CWV agrupados por página con tendencia histórica.
Sí, completamente seguro. Los emojis siguen funcionando en los navegadores modernos mediante los glifos del sistema operativo sin necesidad del script de WordPress. Eliminar ese script ahorra una petición HTTP y unos pocos kilobytes en cada carga de página. Es una de las optimizaciones más sencillas y sin riesgo de toda la lista.

Adrian says:
Hola, muy bueno el post y la web…
te hago una pregunta en la que espero me puedas ayudar… necesito pasar una animación realizada en flash a html5 ya que necesito que se vea por igual en un navegador como en moviles (iphone android, etc.).. Ese paso lo logré usando swiffy de google, que me genera el codigo correspondiente para la animación… El problema me viene a la hora de incrustarlo en una pagina de wordpress (no lo muestra!!). Ya probé de todo y nada, bajé un pluglin llamado html5 swiffy insert, que usa campos personalizados en donde se pega el codigo y se llama por un shortcode, pero tampoco, me genera el espacio en la pagina pero no muestra la animación..
Espero me puedas orientar acerca de alguna forma para poder controlar esto, ya sea con codigo html5 o cambiando el contenido si es que se abre con iphone/ipad.. Desde ya, te agradezco cualquier ayuda …
osman says:
gracias por la excelente recopilacion, son muy interesantes sobre todo para la optimizacion del sitio.