
Una respuesta rápida no siempre es una respuesta actualizada. La caché solo puede ser útil si el sistema tiene una explicación verificable para conservar un dato, detectar que cambió y decidir qué hacer cuando la fuente no responde.
La política correcta depende del recurso y del costo de equivocarse. Un JavaScript versionado puede conservarse durante mucho tiempo; una respuesta personalizada debe restringirse a un caché privado; una API pública puede necesitar una vida corta y un validador. En equipos remotos, documentar ese contrato también evita que una persona en México, otra en otro estado o un cliente revisando el mismo despliegue interpreten “actualizar” de formas distintas.
Empieza por el riesgo, no por el número
| Recurso | Decisión inicial | Riesgo a revisar |
|---|---|---|
| CSS, JavaScript o imagen con hash en la URL | Vida larga y immutable | El build debe generar otra URL cuando cambia el archivo |
| HTML o API pública cambiante | Vida corta y revalidación | Servir contenido desactualizado |
| Respuesta personalizada | private y política cautelosa | Exponer datos de una persona a un caché compartido |
| Respuesta que no debe almacenarse | no-store | Persistir información sensible o efímera |
El estándar RFC 9111 de HTTP Caching describe el objetivo del caching como reutilizar respuestas para reducir tiempo de respuesta y ancho de banda sin cambiar la semántica de la transferencia. Eso no autoriza a elegir un max-age universal: el valor debe relacionarse con la frecuencia de cambio y con el daño que produciría una copia vieja.
Freshness: cuándo una copia todavía sirve
La cabecera Cache-Control contiene directivas para controlar cachés del navegador y cachés compartidos como proxies y CDNs, según la referencia de MDN sobre Cache-Control. Con max-age=600, una respuesta puede considerarse fresca durante 600 segundos desde su generación en el origin. No significa que cada usuario reciba necesariamente el mismo resultado: el tipo de caché, la petición y otras directivas también importan.
Cache-Control: public, max-age=120Dos minutos pueden ser razonables para un catálogo público que cambia con frecuencia; pueden ser insuficientes para un asset inmutable o excesivos para un panel personalizado. En una conexión móvil o una ruta con latencia alta, un hit de caché puede reducir viajes al servidor, pero solo si la respuesta es segura para reutilizarse y el contrato se mantiene coherente.
No-cache no significa no almacenar
no-cache permite almacenar la respuesta, pero exige validarla antes de reutilizarla. no-store expresa la intención de no almacenarla. MDN distingue explícitamente ambas directivas en la documentación de Cache-Control. Confundirlas produce dos errores opuestos: una aplicación puede retener información que no debía persistir o puede eliminar una oportunidad segura de revalidación.
Cache-Control: no-cache
Cache-Control: no-storePara una respuesta después de autenticación o con contenido personalizado, private indica que puede guardarse en un caché privado, como el navegador de esa persona, pero no en uno compartido. Esa frontera importa si el recurso viaja por un CDN usado por varios clientes. Si no sabes qué datos componen la representación, no empieces con public.
Revalidación: comprueba antes de transferir de nuevo
Un ETag identifica una versión de la representación. La referencia de MDN sobre ETag explica que el cliente puede enviar If-None-Match; si la etiqueta sigue coincidiendo, el servidor responde 304 Not Modified sin reenviar el cuerpo completo.
ETag: "catalogo-2026-09-02"
GET /api/productos
If-None-Match: "catalogo-2026-09-02"
HTTP/1.1 304 Not ModifiedLa etiqueta no es un botón de invalidación. Si la representación cambia, el origin debe producir otro valor. Un hash del cuerpo, una revisión de datos o una marca de modificación pueden ser estrategias válidas; lo importante es que dos representaciones diferentes no queden con la misma etiqueta por accidente.
Vary: una URL puede tener variantes
La misma URL puede producir representaciones distintas según Accept-Language, Accept-Encoding u otro header relevante. La guía de caching de MDN muestra cómo Vary informa qué entrada debe participar en la selección de una respuesta almacenada.
Vary: Accept-Language, Accept-EncodingPrueba una política de caché con una respuesta fresca, una respuesta vencida y una fuente temporalmente indisponible. En cada caso registra qué se sirve, qué se vuelve a validar y qué mensaje recibe la persona usuaria.
Assets versionados: la estrategia más sencilla
Si el proceso de build publica app.3a9f1.js y luego app.8b21c.js, cada URL representa un archivo distinto. En ese caso, una política larga puede ser adecuada:
Cache-Control: public, max-age=31536000, immutableLa condición es estricta: nunca sobrescribas el archivo versionado con otro contenido y asegúrate de que el HTML nuevo apunte al nombre nuevo. Si una persona en un equipo remoto recibe HTML antiguo y el asset fue reemplazado en la misma URL, el problema no se corrige aumentando el tiempo de caché; se corrige con versionado confiable y un despliegue coherente.
API con ETag: un ejemplo comprobable
const express = require('express');
const crypto = require('crypto');
const app = express();
let productos = [
{ id: 1, nombre: 'Teclado', precio: 29.90 },
{ id: 2, nombre: 'Mouse', precio: 19.50 }
];
function etagPara(body) {
const hash = crypto.createHash('sha256').update(body).digest('hex');
return `W/"${hash}"`;
}
app.get('/api/productos', (req, res) => {
const body = JSON.stringify(productos);
const etag = etagPara(body);
res.set('Cache-Control', 'public, max-age=120, must-revalidate');
res.set('ETag', etag);
res.type('application/json');
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
res.send(body);
});El ejemplo muestra el flujo, no una configuración universal de producción. Debes decidir cómo serializar de forma estable, cómo tratar autorización, errores, orden de campos y cambios en la fuente de datos. Prueba la primera respuesta y luego copia el valor exacto de ETag:
curl -i http://localhost:3000/api/productos
curl -i http://localhost:3000/api/productos \
-H 'If-None-Match: pega-aqui-el-etag'El diagnóstico debe separar servidor, navegador y CDN
| Síntoma | Primera comprobación | Hipótesis prudente |
|---|---|---|
| Se ve información antigua | Age, ETag, Last-Modified y headers de respuesta | Validador o freshness incorrectos |
| Dos usuarios reciben variantes distintas | Vary, private, cookies y autorización | Respuesta personalizada en un caché compartido |
| El deploy parece no llegar | URL del asset, HTML servido y caché del CDN | Asset no versionado o purga incompleta |
| La aplicación tarda igual | Waterfall, hit/miss, tamaño y origin | El cuello de botella puede estar fuera del caché |
En trabajo remoto, guarda una captura de headers, URL, hora y entorno cuando registres el incidente. “En mi navegador funciona” no permite que otra persona replique la prueba. Un informe breve con request, response, Age, ETag, región aproximada y paso de reproducción convierte una discusión asíncrona en evidencia técnica.
Una política útil para proyectos pequeños
Para un portafolio, una tienda pequeña o una API de un cliente, empieza clasificando cada recurso. Marca si es público o personalizado, si puede tolerar datos antiguos, si su URL cambia al desplegar y quién puede observar la respuesta. Esa clasificación es más útil que copiar una configuración de un tutorial.
Si trabajas desde México para un equipo distribuido, el artículo puede ayudarte a documentar decisiones que cruzan horarios y proveedores de red. No necesitas afirmar que una CDN concreta tiene la misma latencia en todas las ciudades; mide el entorno real del proyecto. El caché es una política de comportamiento, no una promesa de rendimiento universal.
Para relacionar esta práctica con otras capas, revisa la guía interna sobre consumo de APIs con fetch() y el artículo sobre automatización y despliegues. Ambas conexiones ayudan a comprobar si el cliente y el proceso de entrega respetan el contrato que el servidor publica.
Checklist antes de poner caching en producción
- Clasifica el recurso como público, personalizado, versionado o sensible.
- Define el costo de entregar una copia antigua y el tiempo de freshness aceptable.
- Elige
max-age,s-maxage,private,no-cacheono-storesegún esa decisión. - Genera un
ETagnuevo cuando cambie la representación. - Declara variantes con
Varysolo cuando una entrada realmente cambie la respuesta. - Versiona URLs de assets y no sobrescribas archivos inmutables.
- Prueba navegador, caché compartido, CDN y origin por separado.
- Documenta request, response y pasos de reproducción para el equipo remoto.
Un caché bien diseñado no oculta la antigüedad de una respuesta: la hace una decisión explícita. Cuando sabes qué puede reutilizarse, cómo se revalida y qué obliga a publicar una versión nueva, la velocidad deja de competir con la confianza.

Martin Rojas escribe sobre tecnología, programación y desarrollo web. En Skydutz Academy comparte explicaciones prácticas sobre herramientas digitales, conceptos de programación y recursos para aprender de forma progresiva. Su objetivo es hacer que los temas técnicos sean más claros, útiles y accesibles para lectores de distintos niveles.
