Cache delle route

In questa pagina

La cache delle route di Astro memorizza la risposta renderizzata di una pagina on-demand, così un cache hit salta il rendering. EmDash fornisce tag di cache per il contenuto che una pagina renderizza e invalida quei tag quando il contenuto cambia. Le pagine possono restare nella cache per giorni, mentre una pubblicazione di solito diventa visibile in pochi secondi.

La cache degli oggetti memorizza i risultati delle query e continua a renderizzare ogni pagina. Con un provider di cache configurato, i rendering anonimi saltano la cache degli oggetti e leggono dal database, così un rendering che riempie di nuovo una pagina invalidata non proviene da uno snapshot più vecchio (vedi Cosa viene memorizzato nella cache). La cache delle route serve quindi i visitatori anonimi, e la cache degli oggetti accelera soprattutto i rendering per gli editor che hanno effettuato l’accesso.

Scegliere un provider di cache

La cache delle route richiede un provider di cache. Usa quello adatto al tuo deployment:

DeploymentProviderSi importa da
Node.jsmemoryCache()astro/config
CloudflarecacheCloudflare()@astrojs/cloudflare/cache

memoryCache() conserva le voci nella memoria del processo del server. cacheCloudflare() è un provider CDN: memorizza le risposte nella Cloudflare Workers Cache, e un cache hit non esegue mai il Worker. Anche gli adapter di Astro per Netlify e Vercel includono provider CDN; vedi Provider di cache degli adapter. EmDash invalida tramite l’API cache.invalidate() di Astro, indipendente dal provider, quindi i suoi tag funzionano con qualsiasi provider. Differenze tra provider descrive il comportamento che dipende dal provider. astro dev non usa mai la cache, quindi testa la cache con una build di produzione.

Configurare la cache delle route

Registra il provider e dichiara quali route possono essere memorizzate nella cache, per quanto tempo e con quali tag. Questo esempio usa il provider Cloudflare; su Node, usa cache: { provider: memoryCache() } con memoryCache importato da astro/config:

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

export default defineConfig({
	// ...il tuo output, adapter e integrazioni esistenti
	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 è il tempo per cui la cache serve la voce prima che la route venga renderizzata di nuovo. EmDash invalida le voci obsolete alla pubblicazione (vedi sotto), ma un’invalidazione può fallire, quindi maxAge limita anche per quanto tempo una pagina può restare obsoleta, più una risposta obsoleta mentre swr la riconvalida. Consulta Differenze tra provider prima di scegliere un valore alto.
  • swr permette alla cache di continuare a servire una voce obsoleta mentre la riconvalida in background, così i visitatori raramente aspettano un rendering una volta che la pagina è in cache.
  • tags danno un nome alla voce per l’invalidazione. Assegna a ogni route i tag delle collection che renderizza, usando i nomi delle collection: l’invalidazione integrata di EmDash usa i nomi delle collection, quindi i tag corrispondenti vengono invalidati automaticamente. Un tag extra generico su ogni route in cache (qui all, che non è il nome di una collection, quindi EmDash non lo invalida mai da solo) funziona come interruttore di emergenza: invalidarlo, per esempio con context.cache.invalidate({ tags: ["all"] }) in un endpoint che proteggi tu stesso, svuota tutto in un colpo.

Gli helper di query restituiscono anche un cacheHint che puoi passare a Astro.cache.set() all’interno della pagina. I suoi tag indicano la voce esatta (getEmDashEntry), la collection (getEmDashCollection) o la risorsa di chrome (getMenuWithCacheHint, getSiteSettingsWithCacheHint, getTaxonomyTermsWithCacheHint, getWidgetAreaWithCacheHint). Sono gli stessi tag che EmDash invalida alle scritture, quindi una pagina che passa i suoi hint viene invalidata quando viene scritta una voce o una risorsa di chrome che renderizza, comprese quelle che la sua regola di route non nomina:

---
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() si unisce alla regola di route, e i tag si accumulano tra una chiamata e l’altra. Gli hint di cache portano tag ma non maxAge, quindi è la regola di route a decidere per quanto tempo una pagina resta in cache.

Dai a ogni route pubblica una regola esplicita. Le regole corrispondono al percorso della richiesta, quindi elenca le tue route pubbliche una per una: un parametro rest alla radice del sito, come /[...slug], corrisponde anche alle route di amministrazione e API sotto /_emdash/.

L’invalidazione alla pubblicazione è integrata

Le route dell’API dei contenuti di EmDash invalidano la cache delle route ogni volta che cambia il contenuto live: pubblicare, annullare la pubblicazione, eliminare o ripristinare una voce dal cestino e salvare modifiche che raggiungono la voce live invalidano i tag [collection, id]. Creare o duplicare una voce non invalida nulla, perché la nuova voce è una bozza. Qualsiasi regola di route con il tag del nome della collection (come sopra) di solito viene quindi aggiornata entro pochi secondi da una pubblicazione, anche con un maxAge lungo.

Anche il chrome del sito è coperto: salvare le impostazioni del sito invalida emdash:settings, le modifiche ai menu invalidano emdash:menu:{name}, e le scritture di tassonomie e aree widget invalidano i rispettivi tag emdash:taxonomy:{name} / emdash:widget-area:{name}. Le pagine acquisiscono questi tag automaticamente quando usano il cacheHint di getMenuWithCacheHint(), getSiteSettingsWithCacheHint(), getTaxonomyTermsWithCacheHint() o getWidgetAreaWithCacheHint().

I tag del menu coprono solo le modifiche al menu. Pubblicare, annullare la pubblicazione, spostare nel cestino o ripristinare una voce a cui un menu rimanda, oppure cambiare lo slug di una voce collegata o di un termine di tassonomia, non invalida il tag del menu. Una pagina che mostra il menu recepisce tale modifica quando i suoi tag nominano anche quella voce, la sua collezione o la tassonomia di quel termine, altrimenti quando la pagina scade.

Creare, modificare o eliminare un reindirizzamento invalida la pagina in cache al percorso di origine del reindirizzamento, con e senza barra finale, così il reindirizzamento si applica alla richiesta successiva.

Ripristinare una revisione precedente e le modifiche a firme degli autori, libreria multimediale e commenti non invalidano nulla. Le pagine in cache interessate si aggiornano quando scadono o quando la voce che le renderizza viene salvata di nuovo. La pubblicazione programmata invalida i tag delle voci pubblicate solo su Cloudflare; vedi Differenze tra provider.

Per questo conta anche la convenzione dei tag: EmDash invalida i nomi delle collection, gli id delle voci e i tag di chrome emdash:*. Se le tue regole di route usano altri nomi di tag, l’invalidazione integrata non li troverà e le voci obsolete sopravvivranno fino alla scadenza di maxAge.

Tenere le risposte fuori dalla cache

Le regole di route corrispondono al percorso della richiesta, quindi si applicano anche alle risposte che non vuoi memorizzare con un TTL lungo. EmDash tiene automaticamente fuori dalla cache delle route le risposte renderizzate per un utente autenticato, le risposte inviate con Cache-Control: private o no-store, e anche le risposte 404. Le altre risposte di errore e i tuoi reindirizzamenti nel middleware sono compito del template, che li esclude per singola richiesta con Astro.cache.set(false).

Astro.cache.set(false) vale solo fino alla successiva chiamata a Astro.cache.set(). Un layout viene renderizzato dopo il frontmatter della pagina, quindi un layout che imposta un hint di cache riattiva la cache per una pagina che si era esclusa. Passa l’esclusione al layout (per esempio come prop) e salta le sue chiamate a Astro.cache.set() quando è impostata.

Il middleware di EmDash esclude da sé queste risposte dalla cache delle route, quindi non serve alcun codice nel template: le richieste con un token ?_preview= (valido o meno, perché questi URL sono per token e una bozza in cache resterebbe altrimenti raggiungibile dopo la scadenza del token) e le risposte con la barra degli strumenti dell’editor inserita. Con toolbar: "client", lo stesso vale per gli URL ?_edit. Le risposte di anteprima valide e le risposte con la barra degli strumenti inserita vengono inoltre contrassegnate con Cache-Control: private, no-store per i browser e le CDN a valle.

Risposte di errore

EmDash tiene ogni 404 fuori dalla cache delle route, così pubblicare più tardi una voce mancante la mostra subito. Le altre risposte di errore, come un 500 renderizzato su una route in cache, ereditano il TTL della regola. Escludile con Astro.cache.set(false).

Reindirizzamenti nel middleware

Il middleware che va in corto circuito con un reindirizzamento (per esempio la canonicalizzazione dell’host) deve escludersi con context.cache?.set(false). Altrimenti il reindirizzamento viene memorizzato con il TTL della regola di route, e su un provider CDN che non separa le voci di cache per nome host può essere servito anche alle richieste per l’host canonico, causando un loop di reindirizzamento immediato. Usa l’optional chaining: context.cache è undefined in alcune risposte, come la pagina 404 per un URL che non corrisponde ad alcuna route.

Editor e pagine in cache

La cache delle route memorizza una variante condivisa per URL, e la sua chiave di cache ignora i cookie, quindi un cache hit non riflette mai la sessione di un editor. Con la barra degli strumenti renderizzata lato server predefinita, a un editor che naviga nel sito pubblico viene di solito servita la variante anonima in cache, senza la barra degli strumenti, e il cookie emdash-edit-mode non ha effetto su un URL in cache. Una pagina renderizzata per un editor autenticato non viene mai memorizzata nella cache condivisa; la riempie il successivo visitatore anonimo.

Imposta toolbar: "client" per i siti che usano la cache delle route. Un piccolo script mostra quindi il pulsante di modifica agli editor autenticati sulle pagine in cache, e la vista di modifica si carica da un URL ?_edit che viene sempre renderizzato da zero. I link di anteprima aggirano la cache in entrambe le modalità.

Differenze tra provider

Node.js con memoryCache()

  • Ogni processo del server mantiene la propria cache, e un’invalidazione svuota solo il processo che ha gestito la scrittura. Se esegui più processi o istanze, gli altri continuano a servire le loro pagine in cache fino alla scadenza di maxAge.
  • Un riavvio svuota la cache.
  • memoryCache() memorizza solo una risposta che ha un maxAge, quindi una route senza regola non viene mai messa in cache, anche se la pagina chiama Astro.cache.set() con un hint di cache.
  • La pubblicazione programmata non invalida la cache delle route su Node. Una pagina che elenca una voce programmata la mostra quando la sua copia in cache scade, quindi mantieni maxAge breve sulle route che elencano contenuti programmati.

Cloudflare con cacheCloudflare()

  • Il provider scrive gli header di risposta Cloudflare-CDN-Cache-Control e Cache-Tag. La Workers Cache usa entrambi; i client non li vedono mai, e qualsiasi header Cache-Control che le tue pagine impostano per i browser non è influenzato.
  • Quando è configurato un provider di cache, l’adapter @astrojs/cloudflare abilita automaticamente la Workers Cache: la build scrive "cache": { "enabled": true } nella configurazione Wrangler generata che wrangler deploy utilizza.
  • Una route senza regola viene contrassegnata come no-store, ma solo se nulla chiama Astro.cache.set() durante il rendering. Un hint di cache senza maxAge, come quelli di getSiteSettingsWithCacheHint() e getMenuWithCacheHint(), produce Cloudflare-CDN-Cache-Control: public senza durata, quindi la Workers Cache ripiega sulle sue durate di cache predefinite (2 ore per una risposta 200). Se il tuo layout imposta questi hint, ogni pagina che usa il layout diventa memorizzabile nella cache, con o senza regola.
  • Per impostazione predefinita la versione del Worker fa parte della chiave di cache, quindi ogni deploy parte con una cache vuota e le prime richieste successive vengono renderizzate nel Worker. Per mantenere le voci tra un deploy e l’altro, imposta "cache": { "enabled": true, "cross_version_cache": true } nella configurazione Wrangler (vedi Cache tra versioni). Le modifiche ai template diventano quindi visibili solo quando le voci interessate scadono o vengono invalidate.
  • La pubblicazione programmata invalida i tag delle voci che pubblica dal Cron Trigger.

Limiti di frequenza del purge

Il provider Cloudflare invalida eseguendo il purge della Workers Cache. I purge della Workers Cache usano sempre i limiti di purge del piano Free di Cloudflare, qualunque sia il piano del tuo account: 5 richieste al minuto con un burst di 25, per account (vedi Limiti di frequenza del purge). La maggior parte delle scritture di contenuto costa una richiesta di purge ciascuna: i salvataggi che modificano il contenuto live (compresi i salvataggi automatici nelle collection che salvano direttamente nella voce live), la pubblicazione, l’annullamento della pubblicazione, l’eliminazione, il ripristino, la programmazione e l’assegnazione di termini di tassonomia a una voce, oltre a ogni modifica di impostazioni, menu, tassonomie o widget. La scrittura di un reindirizzamento costa una richiesta di purge per variante del percorso di origine, quindi fino a quattro quando cambia l’origine di un reindirizzamento.

Un purge limitato dalla frequenza fallisce senza errore nell’amministrazione, e le pagine interessate mantengono la versione in cache fino alla scadenza di maxAge. Durante modifiche in blocco, o con più editor al lavoro contemporaneamente, scegli un maxAge che puoi accettare come caso peggiore per i contenuti obsoleti.

La cache del browser è separata

La cache delle route può permettersi TTL lunghi perché EmDash la invalida; le cache dei browser non vengono invalidate. Gli hint di EmDash forniscono tag di purge, non validatori Last-Modified automatici né l’header implicito Cache-Control: no-cache di Astro. L’ora di modifica di una singola voce non copre impostazioni, elenchi correlati o voci eliminate, quindi usarla come validatore di pagina può mantenere un body obsoleto attraverso risposte 304 condizionali.

Imposta la cache del browser con Astro.response.headers.set("Cache-Control", ...): usa no-cache per un controllo aggiornato a ogni visita, oppure una durata breve (minuti, non giorni). Per abilitare le richieste condizionali, fornisci un tuo hint lastModified il cui timestamp avanzi ogni volta che cambia una qualsiasi dipendenza della risposta.

Per la data di modifica mostrata di una voce, usa entry.data.updatedAt anziché cacheHint.lastModified, che EmDash non popola automaticamente.