Problema y cuándo aplica
Una aplicación web tiene cuatro tipos de respuestas GET:
/index.htmltiene una URL estable, cambia con cada despliegue y hace referencia a los recursos estáticos actuales./assets/app.8f3c.jstiene un hash de contenido en su nombre de archivo, por lo que un cambio de contenido también cambia la URL./api/cataloges un catálogo de productos público. El servidor selecciona una representación a partir deAccept-Language, y el producto permite aproximadamente 60 segundos de obsolescencia./api/cartestá personalizado para el usuario autenticado y no debe ser reutilizado por una CDN ni por otro usuario.
Después de un lanzamiento, algunos usuarios conservan el HTML antiguo y solicitan un archivo JavaScript antiguo que el despliegue ya eliminó. El catálogo a veces aparece en el idioma incorrecto. El equipo también trata no-cache como "no almacenar", por lo que las soluciones propuestas no describen el comportamiento previsto. Explique cómo interactúan una caché privada del navegador, una caché compartida de CDN, la frescura, la revalidación y las claves de caché. Luego diseñe los encabezados de respuesta, el orden de despliegue y el método de verificación para las cuatro respuestas.
El valor de 60 segundos es una suposición para la entrevista, no una medición de producción de una fuente. El alcance base cubre la caché HTTP. Service Worker Cache, las cachés de memoria de la aplicación y las cachés de datos del framework aparecen en preguntas de seguimiento. Esta es una pregunta de frontend, full-stack o rendimiento web cuya habilidad central es razonar sobre el comportamiento del navegador y la plataforma HTTP, por lo que su categoría es frontend.
Qué evalúa el entrevistador
La primera señal es si el candidato deriva la política a partir de cuatro preguntas: ¿se puede almacenar la respuesta?, ¿quién puede reutilizarla?, ¿cuánto tiempo es fresca? y ¿qué sucede después de que se vuelve obsoleta? Memorizar las directivas de Cache-Control a menudo confunde el almacenamiento con la reutilización directa. Una respuesta sólida define el límite de los datos antes de escribir los encabezados.
La segunda señal es una distinción exacta entre no-cache y no-store. no-cache permite el almacenamiento pero requiere validación con el origen antes de cada reutilización. no-store indica a las cachés que no almacenen la respuesta. Aplicar no-store a cada respuesta dinámica hace perder los beneficios de las solicitudes condicionales y algunas capacidades del navegador; corresponde donde el requisito realmente prohíbe el almacenamiento en la caché HTTP.
La tercera señal es un modelo correcto de frescura y validación. Una respuesta fresca normalmente se puede reutilizar sin contactar a un servidor ascendente. Cuando es obsoleta o se requiere validación, el cliente puede enviar If-None-Match o If-Modified-Since. Si la representación no ha cambiado, el servidor devuelve 304 Not Modified; la caché actualiza los metadatos y reutiliza su cuerpo almacenado.
Finalmente, el entrevistador busca el razonamiento sobre el despliegue y las claves de caché. Unos encabezados de recursos estáticos perfectos no pueden reparar un proceso de lanzamiento en el que el HTML antiguo apunta a archivos eliminados. Omitir Vary: Accept-Language también puede permitir que una caché compartida use una representación en chino para una solicitud en inglés a la misma URL. Una respuesta sólida cubre encabezados, ciclo de vida de los recursos, variantes de representación y pruebas reproducibles.
Preguntas aclaratorias antes de responder
- ¿Pasan las cuatro respuestas a través de una CDN?
s-maxagecontrola las cachés compartidas y no define la frescura en el navegador privado. Sin una CDN, la política de la API pública puede ser más simple. - ¿Proviene el idioma del catálogo de la URL o de un encabezado de solicitud?
/api/catalog?locale=zh-CNya utiliza un URI distinto como parte de la clave de caché. Una sola URL cuya representación depende deAccept-Languagenecesita un campoVaryadecuado. - ¿Puede el navegador almacenar la respuesta del carrito y validarla después? Si la caché privada de un solo usuario puede almacenarla, use
private, no-cache. Si la seguridad o la política del producto prohíben cualquier almacenamiento en caché HTTP, useno-store. - ¿Cuánto tiempo pueden permanecer disponibles los recursos antiguos con hash? La eliminación inmediata rompe pestañas antiguas, navegación por el historial, despliegues escalonados y versiones de reversión (rollback). La retención debe cubrir el período en el que un punto de entrada antiguo sigue siendo accesible más la ventana de reversión.
- ¿Cuán obsoletos pueden llegar a estar los datos del catálogo? "A lo sumo 60 segundos" debe distinguir la frescura normal del uso obsoleto adicional durante la revalidación o errores. Si el contenido más antiguo nunca es aceptable,
stale-while-revalidateystale-if-errorno pueden extender la reutilización. - ¿Hay un Service Worker instalado? Un Service Worker puede interceptar la solicitud y devolver una entrada de Cache Storage antes de que participe la caché HTTP normal. Debe inspeccionarse como una capa separada.
Estructura de respuesta de 30 segundos
"Para cada respuesta primero decido si se puede almacenar, quién puede reutilizarla, cuánto tiempo es fresca y cómo se valida después de eso. Un recurso JavaScript con hash de contenido obtiene un max-age de un año más immutable, con la garantía de que los bytes en la misma URL nunca cambian. El HTML obtiene no-cache y un ETag, por lo que el navegador puede almacenarlo pero lo valida en una reutilización normal. El catálogo público puede dar a la CDN s-maxage=60, agregar un ETag y usar Vary: Accept-Language cuando la misma URL sirve múltiples idiomas. El carrito es al menos private; use private, no-cache si se permite el almacenamiento en el navegador, y no-store solo cuando el almacenamiento en sí esté prohibido. Despliegue los recursos nuevos antes del HTML y conserve los archivos antiguos con hash. Luego pruebe un 200 sin caché, un acierto fresco, revalidación con 304, variantes de idioma y la reproducción de HTML antiguo."
Análisis detallado paso a paso
Paso 1: Modelar el almacenamiento, la reutilización, la frescura y la validación por separado
El almacenamiento en caché HTTP no es un interruptor único. La ruta de una solicitud requiere al menos cuatro decisiones:
- Almacenamiento:
no-storeprohíbe el almacenamiento. Otras respuestas están sujetas al método, estado, autorización y reglas explícitas de almacenamiento en caché. - Alcance de reutilización: Un navegador es una caché privada que normalmente sirve a un usuario. Las CDN y los proxies son cachés compartidas.
privateimpide la reutilización en cachés compartidas, mientras quepublicpuede permitirla explícitamente. - Frescura:
max-age=Nnormalmente define cuánto tiempo las cachés privadas y compartidas pueden reutilizar una respuesta directamente. En una caché compartida,s-maxage=Nanula amax-age. La antigüedad actual de la respuesta también refleja metadatos comoDateyAge. - Comportamiento tras la obsolescencia: Una caché puede enviar una solicitud condicional usando un validador. Una representación sin cambios produce un 304 y la reutilización del cuerpo almacenado; una representación modificada produce un 200 con contenido nuevo.
Por lo tanto, "acierto de caché" describe dos costos diferentes. Un acierto fresco no necesita confirmación aguas arriba. Un acierto revalidado aún incurre en un viaje de ida y vuelta de red (round trip) y trabajo de validación, aunque evita transferir el cuerpo completo de la respuesta. Una respuesta de entrevista debe especificar a cuál de los dos se refiere.
Paso 2: Asignar a los recursos estáticos con hash de contenido un tiempo de vida de frescura prolongado
Si la compilación garantiza que el contenido modificado recibe una nueva URL, un recurso estático puede devolver:
Cache-Control: public, max-age=31536000, immutable31536000 segundos es un año. immutable indica que el recurso no cambiará mientras sea fresco y puede evitar validaciones innecesarias en ciertas rutas de recarga. La parte importante no es el número particular de un año. Es el par de invariantes:
- Los bytes en una URL dada con hash nunca cambian.
- Una nueva versión utiliza una nueva URL referenciada por un nuevo HTML.
Sobrescribir un archivo en la misma URL de larga duración deja a los clientes con contenido antiguo. Eliminar archivos antiguos con hash también rompe pestañas viejas, el historial del navegador, nodos de borde escalonados y versiones de reversión que aún pueden solicitarlos. Un lanzamiento robusto sube primero los recursos nuevos, verifica que sean accesibles, publica el HTML después y retiene los recursos antiguos con hash hasta que los puntos de entrada antiguos y las versiones de reversión ya no sean accesibles.
Paso 3: Revalidar la URL estable del HTML en cada reutilización normal
El HTML de entrada no puede cambiar naturalmente su URL con cada representación, por lo que puede devolver:
Cache-Control: no-cache
ETag: "index-v184"El navegador puede almacenar el documento, pero una reutilización normal requiere validación. Una solicitud posterior incluye:
If-None-Match: "index-v184"El servidor devuelve 304 si la representación no ha cambiado, o 200 con el nuevo HTML y un nuevo ETag después de un lanzamiento. Un ETag identifica una versión de una representación. No tiene que ser un hash del contenido, pero debe cambiar cuando la representación cambie. La respuesta también puede llevar Last-Modified. Si una solicitud contiene tanto If-None-Match como If-Modified-Since, la condición de ETag más precisa tiene prioridad bajo la semántica HTTP.
no-cache no significa "no guardar". Use no-store solo si el producto requiere que el HTML no entre en una caché HTTP en absoluto, aceptando la pérdida de la revalidación 304. La caché de avance/retroceso (back/forward cache) del navegador también puede restaurar una instantánea de la página sin seguir la ruta ordinaria de revalidación HTTP, por lo que el hecho de que "el botón Atrás mostró una página antigua" no puede diagnosticarse únicamente a partir de Cache-Control.
Paso 4: Controlar el comportamiento del navegador y de la CDN de forma independiente para una API pública
Supongamos que el catálogo tolera normalmente 60 segundos de retraso. El navegador debe confirmar en cada solicitud, mientras que la CDN puede reutilizar directamente una respuesta hasta por 60 segundos:
Cache-Control: public, max-age=0, s-maxage=60
ETag: "catalog-v927"
Vary: Accept-LanguageCada directiva tiene una función independiente:
max-age=0permite que el navegador almacene la respuesta pero la vuelve obsoleta de inmediato, por lo que el uso posterior entra en una ruta de validación.s-maxage=60permite que una caché compartida la reutilice directamente durante 60 segundos.ETagpermite que un navegador o una CDN realicen una solicitud condicional tras volverse obsoleta.Vary: Accept-Languageindica a la caché que incluya el encabezado de solicitud de idioma al seleccionar una respuesta almacenada.
Si el producto acepta otros 30 segundos de datos obsoletos a cambio de una menor latencia de validación, agregue stale-while-revalidate=30. Esta directiva estándar afecta a todas las cachés que la admiten, no solo a la CDN. Si solo se debe cambiar el comportamiento de la CDN, utilice una configuración explícita específica de la CDN del proveedor seleccionado. Cuando la garantía del negocio establezca que el contenido nunca debe tener más de 60 segundos, no agregue esta ventana de obsolescencia. Es un intercambio explícito de frescura por menor latencia, no una optimización predeterminada.
A menudo resulta más predecible incluir la configuración regional en la URL, como /api/catalog?locale=zh-CN. El registro, el calentamiento (warming), la invalidación y las claves de caché se vuelven explícitos, y la respuesta ya no necesita Accept-Language para distinguir representaciones en una sola URL. Evite agregar casualmente Vary: User-Agent; su alto número de valores posibles puede arruinar la reutilización en cachés compartidas.
Paso 5: Proteger el carrito personalizado
El carrito nunca debe servirse desde una caché compartida a otro usuario. Si el navegador puede almacenar la respuesta del usuario actual pero debe confirmarla antes de reutilizarla, devuelva:
Cache-Control: private, no-cache
ETag: "cart-v19"private limita el almacenamiento a cachés privadas. no-cache requiere validación antes de la reutilización. No intente aislar a los usuarios con Vary: Cookie: los valores de las cookies tienen una cardinalidad extremadamente alta y crean riesgos tanto para las claves de caché como para la privacidad. Las respuestas personalizadas deben evitar explícitamente la reutilización compartida.
Si el requisito de seguridad es "la respuesta no debe escribirse en ninguna caché HTTP", use:
Cache-Control: no-storeNo es necesario agregar private, no-cache, max-age=0. Las directivas superpuestas o más restrictivas no hacen que la política sea más segura; hacen que su intención sea más difícil de auditar. Además, agregar no-store a respuestas futuras no borra copias frescas almacenadas previamente. La respuesta a incidentes puede requerir una purga de CDN, una URL versionada, la expiración de entradas antiguas o un mecanismo dirigido de limpieza en el navegador.
Paso 6: Vincular la política de caché a un protocolo de despliegue
El fallo del JavaScript eliminado no puede repararse únicamente con encabezados. El protocolo de despliegue debe:
- Subir cada nuevo recurso con hash.
- Verificar desde ubicaciones de borde (edge) de producción que cada recurso sea accesible y tenga el tipo MIME, la compresión y los encabezados correctos.
- Publicar el HTML que hace referencia a las nuevas URL.
- Retener los recursos antiguos con hash durante las ventanas de pestañas antiguas, lanzamientos escalonados y reversiones.
- Restaurar el HTML antiguo durante una reversión mientras sus recursos antiguos aún existan.
- Eliminar un recurso solo después de que ningún punto de entrada accesible haga referencia a él.
Si varios nodos generan HTML, sus ETags también deben corresponder a la representación. Diferentes ETags para un contenido idéntico producen respuestas 200 innecesarias. Reutilizar el mismo ETag fuerte para contenido diferente puede producir un 304 incorrecto. La compresión, el idioma y otros cambios de representación también necesitan claves de caché correctas o el manejo de Vary.
Paso 7: Verificar una secuencia de solicitudes en lugar de una sola recarga
Construya una matriz de solicitudes en DevTools del navegador o con curl:
curl -I "$ORIGIN/index.html"
curl -I -H 'If-None-Match: "index-v184"' "$ORIGIN/index.html"
curl -I -H 'Accept-Language: zh-CN' "$ORIGIN/api/catalog"
curl -I -H 'Accept-Language: en-US' "$ORIGIN/api/catalog"Establezca ORIGIN con el origen del sitio objetivo antes de ejecutar los comandos. Verifique todo lo siguiente:
- Una solicitud inicial devuelve 200, los encabezados previstos y los validadores.
- Un recurso estático no contacta al servidor aguas arriba mientras sea fresco, y una URL modificada recupera un nuevo archivo.
- Una solicitud condicional de HTML sin cambios devuelve 304, mientras que un despliegue produce 200 con un nuevo ETag.
Ageaumenta en los aciertos de la CDN del catálogo, y la validación ocurre después de que expira la frescura compartida.- Las dos solicitudes de idioma reciben cuerpos diferentes y la respuesta incluye
Vary. - El carrito no tiene ninguna configuración que permita la reutilización en caché compartida.
- Reproducir el HTML antiguo aún recupera su recurso con hash referenciado con 200.
- La opción "Disable cache" de DevTools es útil para el diagnóstico de red, pero no reemplaza la prueba de la ruta de caché real.
Pruebe por separado una recarga normal, una recarga forzada, una pestaña nueva, navegación de avance/retroceso y la ruta con un Service Worker. Una recarga forzada cambia las directivas de caché de la solicitud, por lo que probar solo esa ruta no reproduce el comportamiento normal del usuario.
Ejemplo de respuesta de alta calidad
"No empezaría enumerando encabezados. Para cada respuesta respondería cuatro preguntas: ¿se puede almacenar?, ¿quién puede reutilizarla?, ¿cuánto tiempo es fresca? y ¿cómo se valida después de eso?
El archivo JavaScript tiene hash de contenido, por lo que su URL cambia junto con sus bytes. Devolvería public, max-age=31536000, immutable y haría de 'nunca sobrescribir contenido en la misma URL' un invariante de lanzamiento. El HTML de entrada tiene una URL estable, por lo que usaría no-cache con un ETag. Se puede almacenar, pero una reutilización normal lo valida; el contenido sin cambios obtiene un 304, mientras que el contenido modificado obtiene nuevo HTML y un nuevo ETag. no-cache permite el almacenamiento. no-store es la directiva que lo prohíbe.
El catálogo público permite unos 60 segundos de retraso. Usaría max-age=0 para el navegador y s-maxage=60 para la CDN. Si el producto acepta otros 30 segundos de datos obsoletos a cambio de ocultar la latencia de revalidación, agregaría stale-while-revalidate=30; de lo contrario, lo omitiría. Si la misma URL de /api/catalog varía según Accept-Language, necesita Vary: Accept-Language. Incluir la configuración regional en la URL es aún más fácil de razonar.
El carrito es explícitamente privado para el usuario. Si el almacenamiento en el navegador y la validación son aceptables, devolvería private, no-cache. Si la política prohíbe el almacenamiento, devolvería únicamente no-store. No permitiría la respuesta personalizada en una caché compartida ni usaría una cookie de alta cardinalidad como su clave de caché compartida.
El HTML antiguo que solicita un paquete eliminado también expone un error en el protocolo de despliegue. Subiría primero los recursos nuevos, publicaría el HTML en segundo lugar y retendría los recursos antiguos con hash durante la ventana de entradas antiguas y reversión. Mi verificación probaría un 200 inicial, un acierto fresco, un 304 basado en ETag, un 200 post-despliegue, dos representaciones de idioma, el límite de caché compartida del carrito y la reproducción de HTML antiguo; no solo una recarga forzada."
Errores comunes
- Interpretar
no-cachecomo no almacenar → permite el almacenamiento y requiere validación antes de la reutilización → useno-storepara prohibir el almacenamiento, o conserveno-cachemás un validador para beneficiarse del 304. - Asignar a cada recurso un tiempo de vida de frescura de un año → el HTML con URL estable puede seguir apuntando a una versión antigua → reserve la frescura prolongada para recursos direccionados por contenido y valide el documento de entrada.
- Eliminar recursos antiguos con hash inmediatamente después del lanzamiento → pestañas antiguas, historial y versiones de reversión aún pueden hacer referencia a ellos → publique los recursos antes del punto de entrada y reténgalos durante la ventana de accesibilidad.
- Configurar un ETag sin una política de frescura → la caché carece de un tiempo de vida claro de reutilización directa y puede validar innecesariamente → defina la frescura y la validación en conjunto.
- Decir que un 304 es gratis → ahorra el cuerpo de la respuesta pero aún cuesta un viaje de ida y vuelta y trabajo de validación → elija un tiempo de vida fresco cuando la latencia y la tolerancia de los datos lo justifiquen.
- Servir múltiples idiomas en una sola URL sin
Vary→ una caché compartida puede reutilizar la representación incorrecta → coloque el idioma en la URL o agregueVary: Accept-Languagea la clave de caché. - Usar
Vary: Cookiepara aislar carritos → las claves de alta cardinalidad reducen los aciertos y aumentan el riesgo de privacidad → evite la reutilización compartida conprivateono-store. - Asumir que una directiva
no-storerecién agregada borra las entradas antiguas → el nuevo encabezado no puede alcanzar una respuesta fresca que todavía se reutiliza directamente → púrguela, versiónela o espere a que la entrada antigua expire. - Probar únicamente con la recarga forzada de DevTools → la recarga forzada cambia las directivas de caché de la solicitud → pruebe la navegación ordinaria, aciertos frescos, revalidación, restauración del historial y rutas de Service Worker.
Preguntas de seguimiento
Pregunta de seguimiento 1: ¿Puede el catálogo servir datos obsoletos cuando la CDN o el origen fallan?
Divida la tolerancia del negocio en dos ventanas: a lo sumo 60 segundos durante el funcionamiento normal, y una tolerancia separada durante respuestas 5xx del origen. Si los datos más antiguos son aceptables durante un error, evalúe stale-if-error con una duración limitada. Si los precios o el inventario no pueden ser antiguos, exponga el error en su lugar. stale-while-revalidate oculta la latencia de revalidación en segundo plano; stale-if-error permite la reutilización de datos obsoletos durante una falla. Resuelven problemas diferentes.
Pregunta de seguimiento 2: ¿Cuándo debe el servidor utilizar un ETag fuerte o débil?
Un ETag fuerte significa que dos representaciones son equivalentes byte por byte y es adecuado donde las solicitudes de rango exactas o la identidad de bytes importan. Un ETag débil comienza con W/ y representa equivalencia semántica a pesar de posibles diferencias de bytes, lo cual puede adaptarse a cambios de formato irrelevantes en salidas renderizadas por el servidor. La validación de caché puede usar cualquiera de los dos, pero el control de concurrencia con If-Match y las solicitudes de rango requieren otra verificación de las reglas de comparación fuerte.
Pregunta de seguimiento 3: ¿Por qué DevTools dice "from memory cache" o "from disk cache" sin mostrar un 304?
Una respuesta fresca puede reutilizarse sin ninguna solicitud condicional, por lo que no hay un 304. Memoria y disco describen opciones de almacenamiento elegidas por el navegador, no semánticas HTTP independientes. Inspeccione Cache-Control, Age, si se envió una solicitud y la antigüedad actual de la respuesta en lugar de deducir toda la política a partir de una sola etiqueta de DevTools.
Pregunta de seguimiento 4: ¿Por qué cambiar los encabezados de respuesta no ayudó después de agregar un Service Worker?
El Service Worker puede devolver una respuesta antigua de Cache Storage antes de que ocurra una solicitud de red, por lo que la caché HTTP ordinaria nunca participa. Inspeccione el manejador fetch, la versión de Cache Storage, la limpieza en tiempo de activación (activate) y el momento de client-claim. Versione los nombres de caché o el manifiesto de recursos, luego verifique cómo se actualizan las páginas controladas por un Service Worker antiguo.
Pregunta de seguimiento 5: ¿Cómo debe almacenarse en caché el HTML personalizado que hace referencia a recursos con hash?
El HTML puede usar Cache-Control: private, no-cache para evitar la reutilización compartida y validar antes de la reutilización en caché privada. Los recursos estáticos con hash que son idénticos para todos los usuarios aún pueden usar almacenamiento en caché público a largo plazo. Una página autenticada y sus recursos estáticos no necesitan una sola política compartida; establezca el límite a partir de si cada representación está personalizada o no.
Pregunta de seguimiento 6: ¿Cómo puede un despliegue eliminar de forma segura los recursos estáticos antiguos?
Construya un conjunto de referencias a partir del HTML actual, el HTML con capacidad de reversión, los manifiestos de rutas y los registros de lanzamiento; luego agregue la ventana máxima de accesibilidad de páginas antiguas. Un recurso con hash se vuelve elegible para su eliminación retrasada solo después de que ningún punto de entrada publicable haga referencia a él, la ventana de reversión haya finalizado y los registros de acceso no muestren solicitudes válidas. El trabajo de limpieza debe poder detenerse y retener varios lanzamientos recientes para que una mala decisión no destruya la reversión.