Durante mucho tiempo mi estrategia de caché entera fue “probablemente Cloudflare se encarga de esto”, lo cual es cierto en el sentido de que Cloudflare tiene valores por defecto razonables, y falso en el sentido de que nunca había mirado realmente qué le estaban haciendo esos valores por defecto a la salida de mi propio build.
Los assets estáticos de un build de Astro llevan hashes de contenido en sus nombres de archivo —app.a3f92c.css, no app.css— lo que significa que nunca cambian una vez construidos. Ese solo hecho debería significar que son cacheables para siempre, pero las cabeceras por defecto que enviaba Cloudflare Pages solo los cacheaban por unas horas, igual que el HTML.
La solución fue un archivo _headers, que es de lo más sencillo que hay en materia de configuración:
/assets/*
Cache-Control: public, max-age=31536000, immutable
Las páginas HTML siguen teniendo un cacheo de corta duración, ya que pueden cambiar en cada despliegue sin que un cambio de nombre de archivo lo señale. Los assets con hash reciben un año y la flag immutable, que le dice al navegador que ni se moleste en revalidar. Escribir ese único archivo me obligó a explicarme a mí mismo, por escrito, por qué las dos categorías de archivo necesitan reglas distintas —que es exactamente el tipo de entendimiento que “probablemente Cloudflare se encarga de esto” me estaba dejando saltear silenciosamente.