Caché de rutas

En esta página

La caché de rutas de Astro almacena la respuesta renderizada de una página bajo demanda, de modo que un acierto de caché omite el renderizado. EmDash proporciona etiquetas de caché para el contenido que renderiza una página e invalida esas etiquetas cuando el contenido cambia. Las páginas pueden permanecer en caché durante días, mientras que una publicación suele hacerse visible en cuestión de segundos.

La caché de objetos almacena resultados de consultas y sigue renderizando cada página. Con un proveedor de caché configurado, los renderizados anónimos omiten la caché de objetos y leen de la base de datos, de modo que un renderizado que vuelve a llenar una página invalidada no procede de una instantánea más antigua (consulta Qué se almacena en caché). Entonces la caché de rutas sirve a los visitantes anónimos, y la caché de objetos acelera principalmente los renderizados para los editores que han iniciado sesión.

Elegir un proveedor de caché

La caché de rutas necesita un proveedor de caché. Usa el que corresponda a tu despliegue:

DespliegueProveedorSe importa desde
Node.jsmemoryCache()astro/config
CloudflarecacheCloudflare()@astrojs/cloudflare/cache

memoryCache() guarda las entradas en la memoria del proceso del servidor. cacheCloudflare() es un proveedor de CDN: almacena las respuestas en la Cloudflare Workers Cache, y un acierto de caché nunca ejecuta el Worker. Los adaptadores de Astro para Netlify y Vercel también incluyen proveedores de CDN; consulta Proveedores de caché de los adaptadores. EmDash invalida mediante la API cache.invalidate() de Astro, independiente del proveedor, por lo que sus etiquetas funcionan con cualquier proveedor. Diferencias entre proveedores describe el comportamiento que depende del proveedor. astro dev nunca almacena en caché, así que prueba la caché con una compilación de producción.

Configurar la caché de rutas

Registra el proveedor y declara qué rutas pueden almacenarse en caché, durante cuánto tiempo y bajo qué etiquetas. Este ejemplo usa el proveedor de Cloudflare; en Node, usa cache: { provider: memoryCache() } con memoryCache importado desde astro/config:

import { defineConfig } from "astro/config";
import { cacheCloudflare } from "@astrojs/cloudflare/cache";

export default defineConfig({
	// ...tu output, adaptador e integraciones existentes
	cache: {
		provider: cacheCloudflare(),
	},
	routeRules: {
		"/": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
		"/blog": { maxAge: 86_400, swr: 604_800, tags: ["posts", "all"] },
		"/blog/[slug]": { maxAge: 86_400, swr: 604_800, tags: ["posts", "all"] },
		"/sitemap.xml": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
		"/sitemap-[collection].xml": { maxAge: 86_400, swr: 604_800, tags: ["posts", "pages", "all"] },
	},
});
  • maxAge es el tiempo durante el cual la caché sirve la entrada antes de que la ruta se renderice de nuevo. EmDash invalida las entradas obsoletas al publicar (más abajo), pero una invalidación puede fallar, así que maxAge también limita cuánto tiempo puede permanecer obsoleta una página, más una respuesta obsoleta mientras swr la revalida. Revisa Diferencias entre proveedores antes de elegir un valor largo.
  • swr permite que la caché siga sirviendo una entrada obsoleta mientras la revalida en segundo plano, por lo que los visitantes rara vez esperan un renderizado una vez que la página está en caché.
  • tags dan nombre a la entrada para su invalidación. Etiqueta cada ruta con las colecciones que renderiza, usando los nombres de las colecciones: la invalidación integrada de EmDash usa nombres de colección, por lo que las etiquetas que coinciden se invalidan automáticamente. Una etiqueta adicional amplia en cada ruta en caché (aquí all, que no es un nombre de colección, por lo que EmDash nunca la invalida por sí mismo) funciona como interruptor de emergencia: al invalidarla, por ejemplo con context.cache.invalidate({ tags: ["all"] }) en un endpoint que protejas tú mismo, se borra todo de una vez.

Los helpers de consulta también devuelven un cacheHint que puedes pasar a Astro.cache.set() dentro de la página. Sus etiquetas nombran la entrada exacta (getEmDashEntry), la colección (getEmDashCollection) o el recurso del chrome (getMenuWithCacheHint, getSiteSettingsWithCacheHint, getTaxonomyTermsWithCacheHint, getWidgetAreaWithCacheHint). Son las mismas etiquetas que EmDash invalida en las escrituras, por lo que una página que pasa sus hints se invalida cuando se escribe una entrada o un recurso del chrome que renderiza, incluidos los que su regla de ruta no nombra:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
if (!slug) return Astro.rewrite("/404");

const { entry, cacheHint } = await getEmDashEntry("posts", slug);
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---

Astro.cache.set() se combina con la regla de ruta, y las etiquetas se acumulan entre llamadas. Los cache hints llevan etiquetas pero no maxAge, así que la regla de ruta decide cuánto tiempo se almacena una página en caché.

Da a cada ruta pública una regla explícita. Las reglas coinciden con la ruta de la solicitud, así que enumera tus rutas públicas una por una: un parámetro rest en la raíz del sitio, como /[...slug], también coincide con las rutas de administración y de API bajo /_emdash/.

La invalidación al publicar está integrada

Las rutas de la API de contenido de EmDash invalidan la caché de rutas siempre que cambia el contenido publicado: publicar, despublicar, eliminar o restaurar una entrada desde la papelera, y guardar cambios que llegan a la entrada publicada, invalidan las etiquetas [collection, id]. Crear o duplicar una entrada no invalida nada, porque la nueva entrada es un borrador. Por tanto, cualquier regla de ruta etiquetada con el nombre de la colección (como arriba) suele actualizarse en cuestión de segundos tras una publicación, incluso con un maxAge largo.

El chrome del sitio también está cubierto: guardar los ajustes del sitio invalida emdash:settings, las ediciones de menús invalidan emdash:menu:{name}, y las escrituras de taxonomías y áreas de widgets invalidan sus etiquetas correspondientes emdash:taxonomy:{name} / emdash:widget-area:{name}. Las páginas incorporan estas etiquetas automáticamente cuando usan el cacheHint de getMenuWithCacheHint(), getSiteSettingsWithCacheHint(), getTaxonomyTermsWithCacheHint() o getWidgetAreaWithCacheHint().

Las etiquetas de menú cubren solo las ediciones del menú. Publicar, despublicar, enviar a la papelera o restaurar una entrada a la que enlaza un menú, o cambiar el slug de una entrada enlazada o de un término de taxonomía, no invalida la etiqueta del menú. Una página que muestra el menú incorpora ese cambio cuando sus etiquetas también nombran esa entrada, su colección o la taxonomía de ese término, y en caso contrario cuando la página caduca.

Crear, editar o eliminar una redirección invalida la página en caché en la ruta de origen de la redirección, con y sin barra final, de modo que la redirección se aplica en la siguiente solicitud.

Restaurar una revisión anterior y los cambios en firmas de autor, la biblioteca de medios y los comentarios no invalidan nada. Las páginas en caché afectadas por ellos se actualizan cuando caducan o cuando la entrada que las renderiza se vuelve a guardar. La publicación programada invalida las etiquetas de las entradas publicadas solo en Cloudflare; consulta Diferencias entre proveedores.

Por eso también importa la convención de etiquetas: EmDash invalida nombres de colección, ids de entrada y las etiquetas de chrome emdash:*. Si tus reglas de ruta usan otros nombres de etiqueta, la invalidación integrada no coincidirá con ellos y las entradas obsoletas sobrevivirán hasta que expire maxAge.

Mantener respuestas fuera de la caché

Las reglas de ruta coinciden con la ruta de la solicitud, por lo que también se aplican a las respuestas que no quieres almacenar en caché con un TTL largo. EmDash mantiene automáticamente fuera de la caché de rutas las respuestas renderizadas para un usuario con sesión iniciada, las respuestas enviadas con Cache-Control: private o no-store, y también las respuestas 404. Las demás respuestas de error y tus propias redirecciones de middleware son responsabilidad de la plantilla, que las excluye por solicitud con Astro.cache.set(false).

Astro.cache.set(false) solo se mantiene hasta la siguiente llamada a Astro.cache.set(). Un layout se renderiza después del frontmatter de la página, así que un layout que establece un cache hint vuelve a activar la caché en una página que se había excluido. Pasa la exclusión al layout (por ejemplo como prop) y omite sus llamadas a Astro.cache.set() cuando esté establecida.

Los enlaces de vista previa y de edición se gestionan automáticamente

El middleware de EmDash excluye por sí mismo estas respuestas de la caché de rutas, por lo que no se necesita código en la plantilla: las solicitudes con un token ?_preview= (válido o no, porque esas URL son por token y, de lo contrario, un borrador en caché seguiría siendo accesible después de que el token caduque), y las respuestas con la barra de herramientas del editor inyectada. Con toolbar: "client", lo mismo se aplica a las URL ?_edit. Las respuestas de vista previa válidas y las respuestas con la barra de herramientas inyectada se marcan además con Cache-Control: private, no-store para los navegadores y las CDN posteriores.

Respuestas de error

EmDash mantiene todos los 404 fuera de la caché de rutas, de modo que publicar más tarde una entrada que faltaba la muestra de inmediato. Otras respuestas de error, como un 500 renderizado en una ruta en caché, heredan el TTL de la regla. Exclúyelas con Astro.cache.set(false).

Redirecciones en middleware

El middleware que cortocircuita con una redirección (por ejemplo, la canonicalización del host) debe excluirse con context.cache?.set(false). De lo contrario, la redirección se almacena en caché con el TTL de la regla de ruta, y en un proveedor de CDN que no separa las entradas de caché por nombre de host puede servirse también a solicitudes del host canónico, lo que provoca un bucle de redirección instantáneo. Usa encadenamiento opcional: context.cache es undefined en algunas respuestas, como la página 404 de una URL que no coincide con ninguna ruta.

Editores y páginas en caché

La caché de rutas almacena una variante compartida por URL, y su clave de caché ignora las cookies, por lo que un acierto de caché nunca refleja la sesión de un editor. Con la barra de herramientas renderizada en el servidor por defecto, a un editor que navega por el sitio público normalmente se le sirve la variante anónima en caché, sin la barra de herramientas, y la cookie emdash-edit-mode no tiene efecto en una URL en caché. Una página renderizada para un editor con sesión iniciada nunca se almacena en la caché compartida; la rellena el siguiente visitante anónimo.

Establece toolbar: "client" en los sitios que usan caché de rutas. Un pequeño script muestra entonces la píldora de edición a los editores con sesión iniciada en las páginas en caché, y la vista de edición se carga desde una URL ?_edit que siempre se renderiza de nuevo. Los enlaces de vista previa omiten la caché en ambos modos.

Diferencias entre proveedores

Node.js con memoryCache()

  • Cada proceso del servidor mantiene su propia caché, y una invalidación solo limpia el proceso que gestionó la escritura. Si ejecutas varios procesos o instancias, los demás siguen sirviendo sus páginas en caché hasta que expire maxAge.
  • Un reinicio vacía la caché.
  • memoryCache() solo almacena una respuesta que tenga maxAge, así que una ruta sin regla nunca se almacena en caché, aunque la página llame a Astro.cache.set() con un cache hint.
  • La publicación programada no invalida la caché de rutas en Node. Una página que lista una entrada programada la muestra cuando expira su copia en caché, así que mantén maxAge corto en las rutas que listan contenido programado.

Cloudflare con cacheCloudflare()

  • El proveedor escribe los encabezados de respuesta Cloudflare-CDN-Cache-Control y Cache-Tag. La Workers Cache consume ambos; los clientes nunca los ven, y cualquier encabezado Cache-Control que tus páginas establezcan para los navegadores no se ve afectado.
  • Cuando hay un proveedor de caché configurado, el adaptador @astrojs/cloudflare habilita la Workers Cache automáticamente: la compilación escribe "cache": { "enabled": true } en la configuración de Wrangler generada, que recoge wrangler deploy.
  • Una ruta sin regla se marca como no-store, pero solo si nada llama a Astro.cache.set() mientras se renderiza. Un cache hint sin maxAge, como los de getSiteSettingsWithCacheHint() y getMenuWithCacheHint(), produce Cloudflare-CDN-Cache-Control: public sin duración, de modo que la Workers Cache recurre a sus duraciones de caché predeterminadas (2 horas para una respuesta 200). Si tu layout establece estos hints, cada página que usa el layout se vuelve almacenable en caché, con o sin regla.
  • Por defecto, la versión del Worker forma parte de la clave de caché, así que cada despliegue empieza con una caché vacía y las primeras solicitudes posteriores se renderizan en el Worker. Para conservar las entradas entre despliegues, establece "cache": { "enabled": true, "cross_version_cache": true } en tu configuración de Wrangler (consulta Caché entre versiones). Entonces, los cambios de plantilla solo se muestran cuando las entradas afectadas caducan o se invalidan.
  • La publicación programada invalida las etiquetas de las entradas que publica desde el Cron Trigger.

Límites de frecuencia de purga

El proveedor de Cloudflare invalida purgando la Workers Cache. Las purgas de la Workers Cache siempre usan los límites de purga del plan Free de Cloudflare, sea cual sea el plan de tu cuenta: 5 solicitudes por minuto con una ráfaga de 25, por cuenta (consulta Límites de frecuencia de purga). La mayoría de las escrituras de contenido cuestan una solicitud de purga cada una: guardados que cambian el contenido publicado (incluidos los autoguardados en colecciones que guardan directamente en la entrada publicada), publicar, despublicar, eliminar, restaurar, programar y asignar términos de taxonomía a una entrada, además de cada edición de ajustes, menús, taxonomías o widgets. La escritura de una redirección cuesta una solicitud de purga por variante de la ruta de origen, es decir, hasta cuatro cuando cambia el origen de una redirección.

Una purga limitada por frecuencia falla sin error en el administrador, y las páginas afectadas conservan su versión en caché hasta que expire maxAge. Durante ediciones masivas, o con varios editores trabajando a la vez, elige un maxAge que puedas aceptar como el peor caso de contenido obsoleto.

La caché del navegador es independiente

La caché de rutas puede permitirse TTL largos porque EmDash la invalida; las cachés de los navegadores no se invalidan. Los hints de EmDash aportan tags de purga, no validadores Last-Modified automáticos ni el encabezado implícito Cache-Control: no-cache de Astro. La hora de modificación de una sola entrada no cubre los ajustes, las listas relacionadas ni las entradas eliminadas, de modo que usarla como validador de una página puede conservar un cuerpo desactualizado a través de respuestas 304 condicionales.

Configura el almacenamiento en caché del navegador con Astro.response.headers.set("Cache-Control", ...): usa no-cache para una comprobación nueva en cada visita, o una vida útil corta (minutos, no días). Para habilitar las solicitudes condicionales, proporciona tu propio hint lastModified cuya marca de tiempo avance cada vez que cambie cualquier dependencia de la respuesta.

Para la fecha de modificación que se muestra de una entrada, usa entry.data.updatedAt en lugar de cacheHint.lastModified, que EmDash no rellena automáticamente.