← Volver al blog

El orden de invalidación de cachés en WordPress + Cloudflare que NO te explican

Cómo purgar 5 capas de caché en WordPress (OPcache, Object Cache, WP Rocket, LiteSpeed, Cloudflare) sin servir contenido viejo. Orden exacto.

Por SeoNova · Publicado · 9 min de lectura
Diagrama de 5 capas de caché apiladas: navegador, Cloudflare, LiteSpeed, WP Rocket, OPcache/Object Cache. A la izquierda lista numerada del orden de purga de dentro hacia fuera.
Diagrama de 5 capas de caché apiladas: navegador, Cloudflare, LiteSpeed, WP Rocket, OPcache/Object Cache. A la izquierda lista numerada del orden de purga de dentro hacia fuera.

Esta es la historia de cómo un cliente nos llamó histérico un sábado porque “el H1 de la home seguía siendo el viejo después de cuatro horas y de purgar el caché”. Por las prisas del cambio, una capa de caché por debajo seguía sirviendo lo viejo. Después de aquel día escribimos esta nota que llevamos años aplicando.

WordPress moderno tiene 5 capas de caché apiladas. Si las purgas en el orden incorrecto, una capa “más arriba” recachea contenido viejo desde una capa “más abajo” que aún no has limpiado. Es contraintuitivo. Y es lo que rompe deploys cada semana.

Las 5 capas, de más cerca a más lejos del origen

Visualízalo como una cebolla. El origen (tu PHP + MySQL) está dentro. La petición del usuario entra desde fuera y, antes de llegar al origen, atraviesa cada capa. Si encuentra una versión cacheada por el camino, la respuesta se sirve desde ahí sin tocar el origen.

Capa 5 (la más interna) — OPcache + Object Cache

OPcache es bytecode PHP precompilado. Cuando un visitante carga una página y PHP necesita ejecutarse, OPcache evita reinterpretar el código fuente desde cero. Acelera entre 2× y 5× cualquier ejecución de PHP.

Object Cache (normalmente Redis o Memcached) cachea queries de la base de datos. Cada llamada WP_Query, cada get_post(), cada get_option() que ya ejecutaste, queda en RAM. Bajada típica: 50-70 % menos consultas a MySQL.

Cachés vivos en RAM del servidor. No conocen el HTML que sale. Son la base.

Capa 4 — WP Rocket (o plugin equivalente)

WP Rocket genera HTML estático precompilado para cada página de tu WordPress. Cuando llega una petición, en vez de ejecutar PHP, sirve un archivo HTML ya hecho que está en disco (wp-content/cache/wp-rocket/).

Plugin a nivel WordPress. Conoce posts, taxonomías, idioma.

Alternativas equivalentes: LiteSpeed Cache plugin, W3 Total Cache, FlyingPress, WP Super Cache.

Capa 3 — LiteSpeed Web Server (LSWS)

Si tu hosting usa LiteSpeed Web Server como servidor web (en vez de Apache o Nginx), LSWS tiene su propio caché a nivel servidor, no a nivel WordPress.

Es más rápido que WP Rocket porque LSWS sirve la respuesta cacheada sin levantar PHP-FPM. Pero solo aplica si tu hosting es LiteSpeed. Si estás en Nginx + PHP-FPM puro, esta capa no existe (la equivalencia sería Nginx FastCGI cache, otro mundo).

Capa 2 — Cloudflare (edge global)

Cloudflare cachea tu HTML en sus 200+ POPs (puntos de presencia) repartidos por el planeta. Cuando un usuario de Tokio pide tu página, no llega a tu servidor en Madrid — llega al POP de Tokio, que tiene una copia cacheada.

Para WordPress, lo razonable es activar Cache Everything (vía Page Rule o Cache Rule) en URLs públicas, excluyendo /wp-admin/ y /wp-login.php.

Capa 1 (la más externa) — Navegador del usuario

Tu navegador cachea según las cabeceras HTTP que envías:

  • Cache-Control: public, max-age=31536000, immutable → cachea 1 año, no revalida. Para assets versionados (CSS, JS con hash en el nombre).
  • Cache-Control: no-cache → cachea pero revalida cada vez con ETag. Para HTML.
  • Sin Cache-Control → comportamiento por defecto del navegador (impredecible). Mal.

No controlas directamente el caché del usuario. Pero puedes empujar la revalidación enviando ETags correctos y respondiendo 304 cuando no haya cambios.

El orden de purga: de dentro hacia fuera

Aquí está la regla en piedra:

1. OPcache       (solo si despliegas código nuevo)
2. Object Cache  (solo si invalidas algo a nivel queries)
3. WP Rocket     (HTML estático del plugin)
4. LiteSpeed     (caché del servidor)
5. Cloudflare    (edge global)
6. Browser       (ya no controlas, pero las cabeceras ya van bien)

Si purgas Cloudflare primero, Cloudflare hace fetch al origen para repoblar su caché → encuentra que LiteSpeed y WP Rocket aún tienen el HTML viejo → cachea ese HTML viejo → has gastado un purge para nada y sigues sirviendo viejo.

Si purgas Object Cache antes de WP Rocket, WP Rocket sigue sirviendo HTML viejo que se generó con queries que ya cambiaron. Otra purga inútil.

Caso real: cómo se nos rompió

Cambio aparentemente inocente: actualizamos el H1 de la home en un sitio cliente. Cliente publica, ve los cambios desde el editor (que va sin caché), todo bien.

Al día siguiente: usuarios y herramientas externas siguen viendo el H1 viejo. Llamada a soporte: “no carga lo nuevo”.

Lo que pasó:

  1. WP Rocket tenía el HTML viejo en wp-content/cache/wp-rocket/. El plugin tiene un hook que purga al save_post, pero alguien había excluido la home del auto-purge tiempo atrás.
  2. LiteSpeed tenía la versión vieja en RAM. Su auto-purge depende de que WP Rocket o el litespeed-cache plugin le envíe una señal — que no llegó.
  3. Cloudflare tenía la versión vieja en el edge. Edge cache TTL = 1 hora, pero el cliente había acostumbrado al edge a no fallar nunca.

Lo arreglamos manualmente:

1. wp cache flush          (Object Cache → Redis)
2. wp rocket clean         (WP Rocket → HTML estático)
3. LiteSpeed Cache → Purge ALL (panel hosting)
4. Cloudflare → Purge Everything (panel CF)

Cuatro purgas seguidas en el orden correcto. Tres minutos después, el H1 nuevo era visible globalmente.

La automatización: un mu-plugin que escucha eventos

Para no repetir esta secuencia manualmente cada vez que el cliente edita un post, escribimos un mu-plugin (must-use plugin: un PHP que WordPress carga siempre, sin que esté en la lista activable de plugins). Vive en wp-content/mu-plugins/cache-purge-chain.php.

Lo que hace, simplificado:

<?php
add_action('save_post', function ($post_id) {
    // 1. Object Cache
    wp_cache_flush();

    // 2. WP Rocket
    if (function_exists('rocket_clean_post')) {
        rocket_clean_post($post_id);
    }

    // 3. LiteSpeed Cache
    if (defined('LSCWP_V')) {
        do_action('litespeed_purge_post', $post_id);
    }

    // 4. Cloudflare (vía API REST)
    purge_cloudflare_url(get_permalink($post_id));
}, 99, 1);

La función purge_cloudflare_url() llama a la API de Cloudflare con un token de API limitado a purge cache para esa zona. Importante: NO purgar todo Cloudflare en cada save_post — cuesta un purge del cupo gratuito (1000/día). Purga solo la URL afectada.

Una advertencia importante: un mu-plugin no debe llevar JS ni CSS de layout. Hace tiempo metimos código de presentación dentro y se rompió un sitio entero. Mu-plugins son para lógica server-side y nada más.

Cuándo purga global vs purga selectiva

CambioPurga
Edición de un post / páginaSelectiva (solo esa URL)
Nuevo menú o widget en sidebarSelectiva (URLs afectadas)
Cambio de temaGlobal (todo)
Nuevo plugin que afecta a varias páginasGlobal
Actualización de WordPress coreGlobal
Deploy de código PHPGlobal + OPcache reset
Cambio en CSS/JS críticosGlobal + bumpear versión querystring

La purga global es cara: tras hacerla, el primer hit a cada URL fuerza recompilado completo de la pipeline (PHP → HTML → caché). Si tu sitio tiene 10.000 URLs, los primeros 10.000 hits van lentos. Reserva para cambios estructurales.

Lo que no se ve: TTL apropiados

La estrategia complementaria al purge es el TTL (Time To Live: cuánto vive una entrada en caché antes de expirar sola, sin necesidad de purge).

Mis recomendaciones para WordPress:

  • OPcache: opcache.revalidate_freq=60 (revalida cada 60s en producción, 0 en dev).
  • Object Cache: TTL por defecto 1 hora, fragmentos críticos 5 min.
  • WP Rocket: TTL 10 horas (cubre la jornada de un usuario activo).
  • LiteSpeed: TTL 1 día (purga manual cuando hay cambio).
  • Cloudflare: Edge Cache TTL 4 horas para HTML, 1 año para assets versionados (CSS, JS, imágenes con hash).
  • Browser Cache: Cache-Control: no-cache para HTML (siempre revalida con ETag), max-age=31536000, immutable para assets versionados.

Con esos TTLs + purge automático en save_post cubres el 99 % de los casos sin tener que entrar a panel a purgar manualmente.

Lo que SeoNova hace por ti

Toda esta gestión de capas de caché está dentro del WPO Toolkit que estamos empaquetando en SeoNova: instalas el plugin, conecta tus credenciales Cloudflare + LiteSpeed + WP Rocket, y el sistema purga en orden correcto cada vez que editas algo. Sin escribir mu-plugins, sin gestionar API tokens, sin orden mental que recordar.

Si te interesa, únete a la waitlist con 50 % de descuento durante los 3 primeros meses. Lanzamos en otoño 2026.

Preguntas frecuentes

Las dudas que más nos llegan sobre este tema

¿Por qué tantas capas de caché si en teoría sobra con una?
Porque cada capa optimiza una cosa distinta. OPcache cachea PHP compilado (acelera el lenguaje). Object Cache (Redis/Memcached) cachea queries de WP (acelera la base de datos). WP Rocket genera HTML estático (evita ejecutar PHP). LiteSpeed Web Server cachea la respuesta a nivel servidor (más rápido que WP Rocket). Cloudflare cachea en el edge global (sirve sin tocar tu servidor). Quitar cualquiera de las 5 baja tu PSI mobile entre 8 y 25 puntos.
¿No basta con purgar Cloudflare?
No. Cuando purgas Cloudflare, este hace fetch al origen (tu servidor) para cachear la nueva versión. Si LiteSpeed y WP Rocket todavía tienen el HTML viejo, Cloudflare lo cachea otra vez. Has gastado un purge para nada y sigues sirviendo lo viejo. Por eso hay que purgar de **dentro hacia fuera**.
¿Qué pasa con el caché del navegador del usuario?
No lo controlas directamente. Lo que sí controlas es la cabecera HTTP `Cache-Control` que envías. Con un `Cache-Control: public, max-age=31536000, immutable` en assets versionados (CSS, JS con hash) y `Cache-Control: no-cache` en HTML, el navegador cachea solo lo correcto. El HTML siempre se revalida con ETag/If-None-Match → si no cambió, devuelve 304 (sin descarga) y queda rápido. Lo mejor de los dos mundos.
¿Y si solo cambio un widget del footer en una sola página? ¿Purgo todo?
No. Purgas solo esa URL en cada capa. WP Rocket, LiteSpeed y Cloudflare tienen comandos de purge selectivo por URL. La purga global (purge all) se reserva para cambios estructurales: layout nuevo, plugin que afecta a todas las páginas, actualización de tema. La purga global cuesta tiempo de recompilado en el primer hit.
¿OPcache también hay que purgar?
Solo cuando despliegas código PHP nuevo (push de un plugin actualizado, edición de functions.php). OPcache es bytecode PHP compilado, no caché de HTML. Si solo cambias contenido en la base de datos (un post nuevo, un menú), OPcache no necesita purga. Si despliegas código y NO purgas OPcache, PHP sigue ejecutando la versión antigua del código hasta que reinicies PHP-FPM.

Sigue leyendo

Más posts del blog que te pueden interesar