Back

Loading…

Documentation

⚡ Optimizar Drupal empieza por entender la caché

Cuando un sitio Drupal empieza a sentirse lento, es tentador pensar inmediatamente en soluciones como:

  • 🚀 Redis
  • 🌐 Varnish
  • ☁️ Cloudflare o algún CDN
  • 🐘 ajustes de PHP-FPM
  • 🗄️ optimización de base de datos

Todas pueden ser herramientas útiles.

Pero antes de añadir infraestructura, conviene empezar por una pregunta mucho más básica:

¿Drupal está cacheando correctamente lo que ya podría cachear?

Drupal tiene un sistema de caché bastante sofisticado incluido en Core. Si ese sistema no está funcionando como esperamos, añadir otra capa encima puede esconder el problema sin solucionarlo.

Por eso, nuestro primer paso al optimizar un sitio Drupal será siempre entender su comportamiento actual de caché.


🧠 ¿Por qué necesitamos caché?

Cuando Drupal genera una página tiene que realizar una serie de operaciones:

HTTP Request
     ↓
Drupal bootstrap
     ↓
Routing
     ↓
Access checks
     ↓
Load entities
     ↓
Execute Views
     ↓
Render blocks
     ↓
Render entities
     ↓
Twig
     ↓
HTML Response

Muchas de esas operaciones pueden implicar:

  • consultas a la base de datos;
  • ejecución de PHP;
  • carga de configuración;
  • comprobaciones de permisos;
  • renderizado de componentes;
  • ejecución de módulos.

Si hacemos todo ese trabajo en cada request, estamos desperdiciando recursos.

La idea básica de una caché es sencilla:

Primera petición
───────────────
calcular
↓
guardar resultado

Segunda petición compatible
───────────────────────────
reutilizar resultado

Drupal Core incluye mecanismos precisamente para evitar repetir cálculos innecesarios.


🧩 Drupal no tiene una única caché

Cuando decimos:

“La caché de Drupal”

en realidad estamos hablando de varias capas.

Podemos simplificarlo así:

Request
   ↓
Internal Page Cache
   ↓
Dynamic Page Cache
   ↓
Render Cache
   ↓
otras cache bins
   ↓
PHP / Database

Cada capa resuelve un problema diferente.

Esto es importante porque:

Una página puede no estar completamente cacheada y, aun así, Drupal puede estar reutilizando gran parte de su contenido.

Ahí está una de las mayores fortalezas de Drupal.


📄 Internal Page Cache

Drupal Core incluye el módulo:

Internal Page Cache

También conocido técnicamente como:

page_cache

Su objetivo principal es cachear páginas completas para usuarios anónimos. Drupal lo recomienda actualmente para sitios pequeños y medianos y está habilitado por defecto.

La idea es muy eficiente.

Imaginemos:

GET /news

La primera visita puede hacer:

Drupal
↓
database
↓
Views
↓
entities
↓
Twig
↓
HTML

El resultado se guarda.

La siguiente petición anónima compatible puede convertirse conceptualmente en:

GET /news
↓
Page Cache
↓
HTML

Una gran parte del trabajo desaparece.


⚠️ Anonymous no significa necesariamente static

Aquí aparece un detalle importante.

Internal Page Cache asume esencialmente que una página es igual para todos los usuarios anónimos.

Eso funciona perfectamente para páginas como:

/
 /about-us
 /news
 /products

si todos los visitantes anónimos reciben el mismo contenido.

Pero pensemos en un sitio de e-commerce.

Dos usuarios anónimos pueden tener:

Usuario A
Cart: 3 products

Usuario B
Cart: empty

Aunque ambos sean técnicamente anónimos, su página ya tiene información personalizada.

La documentación actual de Drupal advierte precisamente de este caso: sitios que muestran contenido personalizado por sesión a usuarios anónimos, como un shopping cart, pueden necesitar desactivar Internal Page Cache o mover esa personalización a mecanismos dinámicos como JavaScript/AJAX.

Esto será especialmente importante cuando hablemos de Drupal Commerce.


🧩 Dynamic Page Cache

Drupal tiene otra capa fundamental:

Dynamic Page Cache

o:

dynamic_page_cache

Este módulo trabaja de una manera más inteligente.

En lugar de pensar:

Página completa
=
cacheada o no cacheada

Drupal puede separar conceptualmente:

Página
├── Header               ✅ cacheable
├── Menu                 ✅ cacheable
├── Main content         ✅ cacheable
├── Sidebar              ✅ cacheable
└── User-specific block  🔄 dynamic

Dynamic Page Cache puede reutilizar las partes cacheables mientras permite que los elementos personalizados se generen de manera dinámica.

Por eso es útil tanto para usuarios:

anonymous
authenticated

Drupal lo recomienda para sitios de todos los tamaños.


✨ Esa es una de las grandes ventajas del sistema de caché de Drupal

Una página no tiene que ser completamente estática para beneficiarse de la caché.

Podemos tener:

80% cacheable
20% dynamic

y Drupal puede evitar recalcular ese 80% constantemente.

Esto es especialmente importante en sitios complejos con:

  • usuarios autenticados;
  • personalización;
  • permisos;
  • Commerce;
  • Views;
  • bloques;
  • idiomas;
  • contenido contextual.

🏷️ ¿Cómo sabe Drupal qué puede reutilizar?

Aquí entramos en uno de los conceptos más importantes del rendimiento en Drupal:

Cacheability metadata

Drupal maneja principalmente tres propiedades:

Cache Tags
Cache Contexts
Cache Max-Age

Drupal utiliza esta metadata para decidir:

  • cuándo un resultado deja de ser válido;
  • para quién puede reutilizarse;
  • cuánto tiempo puede considerarse válido.

En el siguiente artículo entraremos mucho más profundamente en ellas, pero podemos empezar con una idea sencilla.


🏷️ Cache Tags: ¿de qué datos depende esto?

Podemos pensar:

Cache Tags
=
data dependencies

Imaginemos un bloque que muestra:

Node 42

Drupal puede asociar:

node:42

a ese resultado cacheado.

Si alguien edita el nodo 42:

node:42 changes
↓
invalidate caches tagged node:42

No tenemos que esperar simplemente “10 minutos” para que expire.

Drupal sabe qué contenido quedó obsoleto.


🌍 Cache Contexts: ¿de qué contexto depende?

Podemos pensar:

Cache Contexts
=
request context dependencies

Un componente podría variar según:

language
user permissions
route
URL query parameters
theme

Por ejemplo:

English visitor
→ English version

German visitor
→ German version

No queremos desactivar la caché.

Queremos tener variantes de caché.

Drupal compara este concepto con el comportamiento del header HTTP Vary.


⏱️ Cache Max-Age: ¿cuánto tiempo es válido?

Finalmente:

Cache Max-Age
=
time dependency

Por ejemplo:

max-age: 60

significa que el elemento puede considerarse válido durante 60 segundos.

Pero hay un valor especialmente importante:

max-age: 0

que significa:

No cacheable.

Drupal documenta precisamente esta semántica para Cache API.

Y esto puede tener consecuencias importantes cuando esa metadata se propaga a elementos superiores.


🫧 La cacheabilidad se propaga

Este concepto merece recordarse desde el principio.

Imaginemos:

Page
└── Block
    └── View
        └── Entity

Cada elemento puede aportar metadata de caché.

Drupal va combinando esa información mientras construye la respuesta.

Por eso un componente aparentemente pequeño puede influir en la cacheabilidad de un elemento mucho mayor.

Conceptualmente:

Page
├── cache tags
├── cache contexts
└── max-age
       ↑
       │
metadata proveniente de sus componentes

Esto explica muchos problemas de rendimiento aparentemente misteriosos.

Puede que la página sea perfectamente cacheable…

excepto por un bloque.

Y ese bloque puede estar introduciendo:

max-age: 0

o un contexto especialmente dinámico.


🔎 Antes de optimizar, vamos a mirar los headers

Una de las cosas más sencillas que podemos hacer es preguntar directamente al servidor qué está ocurriendo.

Por ejemplo:

curl -sI https://example.com/

Podemos recibir headers como:

cache-control: max-age=900, public

o:

cache-control: must-revalidate, no-cache, private

La diferencia es enorme.


🟢 Una respuesta pública

Algo como:

cache-control: max-age=900, public

significa que la respuesta HTTP puede ser almacenada durante un tiempo por caches compartidas compatibles.

Esto empieza a abrir la puerta a capas como:

Browser cache
Reverse proxy
CDN

dependiendo de la arquitectura.


🔴 Una respuesta privada

En cambio:

cache-control: must-revalidate, no-cache, private

indica que no estamos entregando una respuesta pública reutilizable por caches HTTP compartidas.

Y esto nos lleva a una regla importante:

Un CDN no puede convertir mágicamente una respuesta privada de Drupal en una buena respuesta pública cacheable.

Primero necesitamos entender por qué Drupal la está marcando así.

Después podremos decidir qué infraestructura externa tiene sentido.


⚙️ Un detalle sorprendente: la configuración de Performance

Drupal tiene esta ruta administrativa:

/admin/config/development/performance

Allí encontramos, entre otras cosas:

Browser and proxy cache maximum age

Esta configuración afecta los headers HTTP utilizados para browsers y proxies.

La documentación actual de Drupal muestra un detalle especialmente interesante.

Si el valor está configurado a 0, Drupal puede responder:

cache-control: must-revalidate, no-cache, private

Mientras que un valor positivo puede producir algo como:

cache-control: max-age=900, public

Esto significa que una simple configuración puede tener consecuencias enormes para cualquier estrategia posterior con:

Varnish
CDN
Browser Cache

⚠️ Pero cuidado: esto no es lo mismo que la caché interna

Aquí hay una distinción muy importante.

El valor:

Browser and proxy cache maximum age

controla los headers HTTP para browsers y proxies.

No significa que Internal Page Cache solamente guarde la página durante ese tiempo.

Drupal documenta que Internal Page Cache puede mantener sus páginas cacheadas hasta que sean invalidadas, independientemente de ese valor.

Tenemos por tanto dos conceptos diferentes:

Drupal internal cache
≠
HTTP cache lifetime

Esta diferencia será muy importante más adelante.


🧪 Una prueba muy sencilla

Cuando analizamos rendimiento podemos empezar haciendo varias veces:

curl -sI https://example.com/
curl -sI https://example.com/
curl -sI https://example.com/

¿Por qué varias?

Porque queremos comparar:

First request
vs
Second request
vs
Third request

Dependiendo de nuestra configuración y headers de debugging, podremos observar si Drupal empieza a reutilizar respuestas.

En un entorno que exponga los headers correspondientes podemos encontrar indicadores relacionados con:

X-Drupal-Cache
X-Drupal-Dynamic-Cache

y comenzar a distinguir:

HIT
MISS
UNCACHEABLE

No todos estos headers tienen por qué exponerse de la misma forma en todos los entornos, pero son muy útiles durante diagnóstico.


🕵️ MISS y UNCACHEABLE no significan lo mismo

Esta distinción es crucial.

Un:

MISS

puede significar:

“No estaba en caché todavía.”

La próxima petición podría convertirse en un:

HIT

Pero:

UNCACHEABLE

significa algo diferente:

Drupal ha determinado que esa respuesta no puede utilizar normalmente esa capa de caché.

Eso nos obliga a investigar.

Puede haber:

  • sesiones;
  • cookies;
  • responses especiales;
  • cache contexts;
  • max-age: 0;
  • código personalizado;
  • módulos contrib;
  • comportamiento específico de Commerce.

Es ahí donde empieza el verdadero diagnóstico.


🧰 Drupal puede mostrarnos todavía más información

Cuando necesitamos investigar profundamente un problema de caché, Drupal puede exponer metadata adicional.

En desarrollo podemos obtener headers como:

X-Drupal-Cache-Contexts
X-Drupal-Cache-Tags

Drupal documenta estos headers precisamente como una herramienta para ver los contexts y tags que afectan a una página.

También existe debugging de Render Cache que permite mostrar metadata dentro del HTML durante desarrollo.

Esto será particularmente útil cuando lleguemos al artículo dedicado al diagnóstico.


🚀 Entonces, ¿cuándo hablamos de Redis?

Todavía no.

Redis puede ser una excelente herramienta para Drupal.

Pero Redis responde principalmente a otra pregunta:

¿Dónde y cómo almacenamos determinados cache bins?

No responde por sí solo:

¿Es esta respuesta cacheable?

Si Drupal decide:

max-age: 0

o:

UNCACHEABLE

cambiar el backend de caché de:

Database

a:

Redis

no corrige automáticamente esa decisión.


🌐 ¿Y un CDN?

Lo mismo.

Un CDN es extraordinariamente eficiente cuando puede recibir algo como:

Cache-Control: public, max-age=...

y reutilizar esa respuesta para miles de visitantes.

Pero si Drupal devuelve:

Cache-Control: private, no-cache

debemos entender primero por qué.

Por eso nuestro orden será:

1. Cacheability de Drupal
        ↓
2. Internal/Dynamic Page Cache
        ↓
3. HTTP caching correcto
        ↓
4. Reverse proxy / CDN
        ↓
5. optimizaciones adicionales

No al revés.


🎯 Nuestro primer checklist

Antes de hablar de infraestructura avanzada podemos revisar:

✅ ¿Están habilitados los módulos de Core?

Internal Page Cache
Dynamic Page Cache

✅ ¿Qué tenemos configurado en?

/admin/config/development/performance

✅ ¿Qué responde realmente producción?

curl -sI https://example.com/

✅ ¿Qué ocurre en requests consecutivos?

HIT?
MISS?
UNCACHEABLE?

✅ ¿Estamos evaluando usuarios anónimos o autenticados?

No son el mismo escenario.

✅ ¿Existe personalización por sesión?

Especialmente importante en Commerce.

✅ ¿Drupal está diciendo public o private?

Esto determinará lo que podremos hacer posteriormente con caches HTTP externos.


🏗️ Optimización por capas

Nuestra filosofía para esta serie será no intentar mejorar todo al mismo tiempo.

Pensaremos Drupal como una arquitectura por capas:

Browser
   ↓
CDN
   ↓
Reverse Proxy
   ↓
Drupal Page Cache
   ↓
Drupal Dynamic/Render Cache
   ↓
Cache Backend
   ↓
PHP
   ↓
Database

Cada capa tiene una responsabilidad diferente.

Y cada una puede convertirse en un cuello de botella.

Pero si empezamos desde Drupal y avanzamos hacia afuera y hacia abajo, podremos saber qué problema estamos resolviendo realmente.


✅ La idea principal

Si nos quedamos con una sola idea de este primer artículo debería ser esta:

Antes de añadir una nueva capa de caché, debemos comprobar que Drupal esté produciendo correctamente contenido cacheable.

Drupal ya incluye mecanismos muy potentes:

📄 Internal Page Cache para páginas anónimas, 🧩 Dynamic Page Cache para páginas parcialmente dinámicas, 🏷️ cache tags para invalidación, 🌍 cache contexts para variaciones, ⏱️ max-age para dependencias temporales.

Una vez que entendamos cómo trabajan juntas estas piezas, podremos empezar a optimizar con mucha más precisión.

Y, sobre todo, podremos evitar una de las formas más comunes de “optimización”:

añadir tecnología sin saber todavía dónde está el problema.


🔜 En el siguiente artículo

Hemos mencionado varias veces tres conceptos:

cache tags
cache contexts
cache max-age

Son el verdadero lenguaje con el que Drupal describe la cacheabilidad.

¿Por qué un bloque cambia cuando cambia un nodo? ¿Cómo puede Drupal cachear contenido diferente según idioma o permisos? ¿Por qué max-age: 0 puede ser tan problemático? ¿Y cómo se propaga toda esa información desde un pequeño render array hasta una página completa?

👉 En el siguiente artículo entraremos en 🧠 Cacheability en Drupal: cómo funcionan tags, contexts y max-age.

Contents