Una introducción en profundidad al almacenamiento en caché HTTP: Cache-Control & Vary

Introducción - alcance del artículo

Esta serie de artículos trata sobre el almacenamiento en caché en el contexto de HTTP. Cuando se realiza correctamente, el almacenamiento en caché puede aumentar el rendimiento de su aplicación en un orden de magnitud. Por el contrario, cuando se pasa por alto o se ignora por completo, puede provocar algunos efectos secundarios muy no deseados causados ​​por el mal comportamiento de los servidores proxy que, en ausencia de instrucciones claras de almacenamiento en caché, deciden almacenar en caché de todos modos y servir recursos obsoletos.
En la primera parte de esta serie, argumentamos que el almacenamiento en caché es la forma más efectiva de aumentar el rendimiento, cuando se mide por el tiempo de carga de la página. En esta segunda parte, es hora de cambiar nuestro enfoque a los mecanismos a nuestra disposición. Para decirlo de otra manera: ¿cómo funciona realmente el almacenamiento en caché HTTP?
Para responder a esta pregunta, decidimos considerar el caso de una memoria caché vacía que comienza a almacenar y almacenar recursos progresivamente. A medida que recibe gradualmente las solicitudes HTTP entrantes, nuestro caché comenzará a comportarse en consecuencia. Sirviendo el recurso desde el caché cuando hay una copia nueva disponible, variando en múltiples representaciones, haciendo una solicitud condicional ... De esta manera, podemos introducir cada concepto progresivamente según lo necesitemos.
Al principio, nuestro caché vacío no tendrá más remedio que reenviar solicitudes al servidor de origen. Esto nos permitirá comprender cómo los servidores de origen instruyen a nuestra memoria caché sobre qué hacer con el recurso, por ejemplo, si se le permite almacenarlo y por cuánto tiempo. Para esto, examinaremos cada directiva de Cache-Control y aclararemos algunos de los cuales se sabe que tienen significados conflictivos .
En segundo lugar, veremos qué sucede cuando nuestro caché recibe una solicitud de un recurso que ya conoce. ¿Cómo decide nuestro caché si puede reutilizar una respuesta previamente almacenada? ¿Cómo se asigna una solicitud HTTP dada a un recurso en particular? Para responderlas, aprenderemos sobre las variaciones de representación con el encabezado Vary.
Este artículo se centrará en el conocimiento más valioso desde la perspectiva de un desarrollador web. Por lo tanto, las solicitudes condicionales solo se analizan brevemente y serán el foco de otro artículo.
Sin más preámbulos, comencemos con una descripción general de lo que exploraremos.

El árbol de decisión de almacenamiento en caché HTTP

Conceptualmente, un sistema de caché siempre involucra al menos a tres participantes. Con HTTP, estos participantes son el cliente, el servidor y el proxy de almacenamiento en caché.
Sin embargo, cuando aprenda sobre el almacenamiento en caché HTTP, le recomendamos encarecidamente que no piense en el cliente como su navegador web típico porque en la actualidad, todos se envían con su propia capa de almacenamiento en caché HTTP. Hace que sea difícil separar claramente el navegador del caché. Por este motivo, lo invitamos a pensar en el cliente como un programa de línea de comandos sin cabeza, como cURL o cualquier aplicación sin un caché HTTP incorporado.
Dejando a un lado todas las precauciones, ahora profundicemos en el tema observando la siguiente imagen: el árbol de decisión de almacenamiento en caché HTTP.

Esta imagen ilustra todas las rutas posibles que puede tomar una solicitud cada vez que un cliente solicita un recurso a un servidor de origen detrás de un sistema de almacenamiento en caché. Un examen cuidadoso de esta ilustración revela que solo hay cuatro resultados posibles.
Claramente, separar estos resultados en nuestras mentes es realmente muy conveniente, ya que cada concepto de almacenamiento en caché importante (instrucciones de caché, coincidencia de representación, solicitudes condicionales y envejecimiento de recursos) se asigna a cada uno de ellos.
Describamos sucintamente cada uno introduciendo dos términos importantes relacionados con la terminología de almacenamiento en caché HTTP: aciertos de caché y errores de caché.

Golpea y falla

El primer resultado posible es cuando el caché encuentra un recurso coincidente y se le permite servirlo, lo que, en el mundo del almacenamiento en caché, son dos cosas distintas. Este resultado es lo que comúnmente llamamos un acierto de caché, y es la razón por la que usamos cachés en primer lugar.
Cuando ocurre un golpe de caché, descarga completamente el servidor de origen y la latencia se reduce drásticamente. De hecho, cuando el golpe de caché ocurre en la latencia de caché HTTP del navegador es nulo y el recurso solicitado está disponible instantáneamente.
Desafortunadamente, los éxitos de caché representan solo uno de los cuatro resultados posibles. El resto de ellos caen en la segunda categoría, también conocida como errores de caché, que pueden suceder por solo tres razones.
La primera razón por la que ocurre una falla de caché es simplemente cuando el caché no encuentra ningún recurso coincidente en su almacenamiento. Esto suele ser una señal de que el recurso nunca se ha solicitado antes, o se ha desalojado de la memoria caché para liberar espacio. En tales casos, el proxy no tiene más remedio que reenviar la solicitud al servidor de origen, descargar completamente la respuesta y buscar instrucciones de almacenamiento en caché en los encabezados de respuesta.
La segunda razón por la que puede ocurrir una pérdida de memoria caché es en realidad tan perjudicial, donde la memoria caché detecta una representación coincidente, una que podría utilizar. Sin embargo, el recurso ya no se considera como nuevo , veremos cómo exactamente en la sección de control de caché de este artículo, pero se dice que está obsoleto.
En tal caso, el caché envía un tipo especial de solicitud, llamada solicitud condicional al servidor de origen. Las solicitudes condicionales permiten que las cachés recuperen recursos solo si son diferentes de las que tienen en su almacenamiento local. Dado que solo el servidor de origen tiene la representación más reciente de un recurso dado, las solicitudes condicionales siempre tienen que pasar por toda la cadena de proxy de almacenamiento en caché hasta el servidor de origen.
Estas solicitudes especiales solo tienen dos resultados posibles. Si el recurso no ha cambiado, se le indica al caché que use su copia local al recibir una respuesta 304 No Modificada junto con encabezados actualizados y un cuerpo vacío. Este resultado, el tercero en nuestra lista, se llama validación exitosa.
Finalmente, el último resultado posible es cuando el recurso ha cambiado. En este caso, el servidor de origen envía una respuesta normal de 200 OK, como lo haría si el caché estuviera vacío y hubiera reenviado la solicitud. Para decirlo de otra manera, los errores de caché causados ​​por el caché vacío y la validación fallida producen exactamente la misma respuesta HTTP.
Para visualizar mejor estos cuatro caminos, es útil visualizarlos en una línea de tiempo, como se ilustra a continuación.

Al principio, el caché está vacío. El flujo de solicitudes comienza con un error de caché (resultado de caché vacío). En su camino de regreso, el caché leería las instrucciones de almacenamiento en caché y almacenaría la respuesta. Todas las solicitudes posteriores para este recurso en particular darían lugar a aciertos de caché, hasta que el recurso se vuelva obsoleto y necesite ser revalidado.
Tras una primera revalidación, es posible que el recurso no haya cambiado, por lo tanto, se enviaría un 304 No modificado.
Luego, el recurso finalmente se actualiza por un cliente, generalmente con una solicitud PUT o PATCH. Cuando llega la siguiente solicitud condicional, el servidor de origen detecta que el recurso ha cambiado y responde 200 OK con los encabezados ETag y Last-Modified actualizados.
Conocer los aciertos y errores de caché junto con las 4 posibles rutas que podría tomar cada solicitud almacenable en caché, debería darle una buena visión general de cómo funciona el almacenamiento en caché.
Aunque las descripciones generales solo pueden llevarte lejos. En la siguiente sección, daremos una explicación detallada de cómo los servidores de origen comunican las instrucciones de almacenamiento en caché.

Cómo los servidores de origen comunican las instrucciones de almacenamiento en caché

Los servidores de origen comunican sus instrucciones de almacenamiento en caché a los servidores proxy de almacenamiento en caché posteriores agregando un encabezado Cache-Control a su respuesta. Este encabezado es una adición de HTTP / 1.1 y reemplaza el encabezado Pragma en desuso, que nunca fue estándar.

Los valores de encabezado de control de caché se denominan directivas. La especificación define muchos de ellos, con varios usos y soporte de navegador . Los desarrolladores utilizan principalmente estas directivas para comunicar las instrucciones de almacenamiento en caché. Sin embargo, cuando está presente en una solicitud HTTP, los clientes también pueden influir en la decisión de almacenamiento en caché. Tomemos ahora el tiempo para describir las directivas más útiles.

edad máxima

La primera directiva importante de Cache-Control que debe conocer es la directiva max-age, que permite a un servidor especificar la duración de una representación. Se expresa en segundos. Por ejemplo, si un caché ve una respuesta que contiene el encabezado Cache-Control: max-age = 3600, se le permite almacenar y servir la misma respuesta para todas las solicitudes posteriores de este recurso durante los próximos 3600 segundos.

Durante estos 3600 segundos, el recurso se considerará nuevo y se producirán coincidencias de caché. Pasado este retraso, el recurso se volverá obsoleto y la validación se hará cargo.

no-store, no-cache, must-revalidate

A diferencia de max-age, las directivas no-store, no-cache y must-revalidate tratan de ordenar a los cachés que no almacenen en caché un recurso. Sin embargo, difieren en formas sutiles.

no-store se explica por sí mismo y, de hecho, hace incluso un poco más de lo que sugiere su nombre. Cuando está presente, una caché compatible con HTTP / 1.1 no debe intentar almacenar nada, y también debe tomar medidas para eliminar cualquier copia que pueda tener, ya sea en la memoria o almacenada en el disco.

La directiva sin caché, por otro lado, podría decirse que es mucho menos explicativa. Esta directiva en realidad significa nunca usar una copia local sin validar primero con el servidor de origen. Al hacerlo, evita toda posibilidad de un golpe de caché, incluso con recursos nuevos.

Para decirlo de otra manera, la directiva sin caché dice que los cachés deben revalidar sus representaciones con el servidor de origen. Pero luego viene otra directiva, llamada torpemente ... debe-revalidar.

Si esto comienza a ser confuso para usted, tenga la seguridad de que no está solo. Si lo que uno quiere es no almacenar en caché, tiene que usar no-store en lugar de no-cache. Y si lo que uno quiere es revalidar siempre, debe usar no-cache en lugar de must-revalidate.

Confuso, de hecho.

En cuanto a la directiva must-revalidate, se usa para prohibir que un caché sirva un recurso obsoleto. Si un recurso es nuevo, must-revalidate permite perfectamente que un caché lo sirva sin forzar ninguna revalidación, a diferencia de no-store y no-cache. Es por eso que este encabezado siempre debe usarse con una directiva de edad máxima, para indicar un deseo de almacenar en caché un recurso durante un tiempo y cuando se vuelve obsoleto, aplicar una revalidación.

Cuando se trata de estas tres últimas directivas, encontramos la elección de palabras para describir cada una de ellas particularmente confusa: no-store y no-cache se expresan negativamente, mientras que must-revalidate se expresa positivamente. Sus diferencias probablemente serían más obvias si se expresaran de la misma manera.

Por lo tanto, es útil pensar en cada uno de ellos expresado en términos de lo que no está permitido:
  • sin tienda: nunca almacene nada
  • no-cache: nunca golpe de caché
  • debe revalidar: nunca sirva rancio
Técnicamente, estas directivas pueden aparecer en el mismo encabezado de Cache-Control. No es raro verlos combinados como una lista de valores separados por comas. Muchos sitios web populares todavía parecen comportarse de manera muy conservadora, enviando páginas HTML con el siguiente encabezado:
Control de caché: no-cache, no-store, max-age = 0, must-revalidate

Cuando te topas con esto, la intención detrás de esto generalmente es bastante clara: el equipo de desarrollo web quiere asegurarse de que el recurso nunca se atienda obsoleto a cualquiera.

Sin embargo, tales líneas de cache-buster probablemente ya no sean necesarias. El trabajo anterior realizado en 2017 ya demostró que los navegadores realmente cumplen con la especificación con respecto a las directivas de respuesta de Cache-Control. Por lo tanto, a menos que esté planeando configurar una pila de almacenamiento en caché con software de décadas de antigüedad, debería estar bien usando solo las directivas que necesita. Las combinaciones más populares se analizarán en otro artículo.

publico privado

Las últimas directivas importantes que aún no hemos discutido son un poco diferentes, ya que controlan qué tipos de cachés pueden almacenar en caché los recursos. Estas son las directivas públicas y privadas, la privada es la predeterminada si no se especifica.

Las cachés privadas son las que se supone que deben ser utilizadas por un solo usuario. Por lo general, este es el caché del navegador web. CDN y servidores proxy inversos, por el contrario, manejan solicitudes provenientes de múltiples usuarios.
¿Por qué necesitamos distinguir estos dos tipos de cachés? La respuesta es sencilla: seguridad, como se ilustra en el siguiente ejemplo.
Muchas aplicaciones web exponen puntos finales de conveniencia que dependen de información proveniente de otro lugar que no sea la URL. Si dos usuarios acceden a su perfil solicitando / users / me, en   https: //api.example/com , y su identificación de usuario real está oculta dentro de una Autorización: Portador 4Ja23ç42…. token, el caché no podrá decir que estos son, de hecho, dos recursos muy diferentes.
De hecho, al construir su clave de caché, los cachés no inspeccionan los encabezados HTTP a menos que se les indique específicamente, como veremos en la siguiente sección.

s-maxage

La directiva s-maxage es como la directiva max-age, excepto que solo se aplica a cachés públicos, que también se conocen como cachés compartidos (de ahí el prefijo s-). Si ambas directivas están presentes, s-maxage tendrá prioridad sobre max-age en cachés públicas y se ignorará en las privadas.
Cuando se usa esta directiva, la regla general es asegurarse siempre de que el valor s-maxage esté por debajo de max-age. El razonamiento detrás de esta regla es que cuanto más cerca esté del origen, más adecuado es verificar con frecuencia cuál es la última representación.
Imagina que estuvieras en caché durante un día en el proxy y una hora en los navegadores.
Cada vez que un navegador solicite un recurso a servidores ascendentes, podríamos saber de antemano que el proxy no se pondrá en contacto con el servidor de origen durante al menos un día. Por lo tanto, ¿por qué no poner el mismo TTL directamente en los navegadores? Como conclusión, es una buena práctica dejar siempre fuera un TTL más largo en max-age que en s-maxage.
revalidar obsoleto y error si obsoleto
Estas dos directivas no son técnicamente parte de la especificación original, pero son parte de una extensión que se describió por primera vez hace más de 10 años. Aunque el soporte de su navegador es limitado, ¡ algunos CDN populares los han apoyado por más de 5 años!
Aunque revalidar rancio mientras es bastante útil. Como su nombre lo indica, permite que un caché "devuelva [...] inmediatamente una respuesta obsoleta mientras la revalida en segundo plano, ocultando así la latencia (tanto en la red como en el servidor) de los clientes".
Esta extensión de almacenamiento en caché resulta realmente útil para cosas como imágenes, donde reducir la latencia es fundamental para la experiencia del usuario, y donde tener una versión obsoleta durante unos segundos a menudo es mejor que una descarga de imágenes dolorosa.
En cuanto a error obsoleto, permite que un caché sirva una versión obsoleta si el servidor de origen devuelve un código de estado 5xx. Esto brinda a los desarrolladores la oportunidad de solucionar posibles problemas durante un período de gracia en el que los clientes están protegidos de las páginas de errores irritantes.
Considere el caso de un script meteo de terceros. Si el servidor meteo no se puede alcanzar durante unos minutos, probablemente sea mejor mostrar un pronóstico ligeramente desactualizado durante este lapso de tiempo, que ver una parte de la página en blanco (o una página en blanco completa si el código lo hace) no maneje los scripts de terceros al cargar fallas.

Lo que aún no sabemos

Después de examinar estas directivas de Cache-Control, ahora entendemos cómo las aplicaciones que se distribuyen en la web tienden a aprovechar los mecanismos de almacenamiento en caché HTTP de múltiples maneras, dependiendo de lo que necesiten.
Aunque lo que aún no entendemos es qué hacen realmente los softwares de caché con la respuesta que reciben. Lo más probable es que tengan que almacenarlo en algún lugar para recuperarlo más tarde. Esa es la idea central de cualquier sistema de almacenamiento en caché después de todo.
En circunstancias normales, esto ciertamente se parece a lo que llamaríamos un detalle de implementación. Debería bastar con saber que los recursos se almacenan de alguna manera. Sin embargo, en este caso, aprender un poco más es realmente crítico.
Descuidar los mecanismos que rigen cómo los softwares de almacenamiento en caché asignan objetos del espacio de respuestas HTTP a su espacio de almacenamiento puede tener consecuencias realmente inesperadas, como servir un documento chino codificado en brotli, a un usuario que no entiende chino, usando un navegador que no puede decodificar brotli ¯ \ _ (ツ) _ / ¯

Cómo los cachés almacenan y recuperan recursos

Aunque es poco probable que suceda, ya que la mayoría de los navegadores pueden decodificar brotli, y dado que la mayoría de las personas saben cómo 說 中文, la situación anterior aún puede ocurrir fácilmente. Para entender por qué este es el caso, uno debe considerar cómo los cachés almacenan sus representaciones.
En virtud de lo que intentan lograr, la mayoría de los softwares de almacenamiento en caché deberían poder recuperar rápidamente documentos de texto simples. Para hacerlo, una estrategia muy simple pero poderosa es usar una tienda de valores clave. Esta estrategia se ajusta bien a las representaciones en memoria. Por lo tanto, la pregunta que uno debe responder al diseñar es la siguiente: ¿cómo construir una clave de caché a partir de una respuesta HTTP?
Lo que estamos buscando aquí es una forma de identificar un recurso de manera única Convenientemente, ¡esta es exactamente la razón por la cual los URI s - Identificadores uniformes de recursos - fueron inventados en primer lugar!
Pero los URI no dicen toda la verdad sobre los recursos. Nunca los describen por completo, aunque solo sea por el hecho de que los recursos cambian con el tiempo.
Los sitios web se renombran, se publica nuevo contenido y los usuarios actualizan su perfil. Por supuesto, no por las mismas razones o con la misma frecuencia, aunque todos los recursos eventualmente cambiarán. De hecho, toda la especificación de solicitud condicional se basa en esta única observación: nada es permanente excepto el cambio .
Dejando a un lado las citas filosóficas, hay, sin embargo, otra razón independiente del tiempo por la cual cambian los recursos. De hecho, en cualquier momento, los recursos pueden estar disponibles en múltiples representaciones. Es por eso que tenemos Content-Negociation.
Los encabezados de solicitud HTTP Accept, Accept-Language, Accept-Encoding, Accept-Charset (y algunos otros encabezados que no son estrictamente parte de la negociación de contenido) agregan otra dimensión en la que las representaciones pueden diferir. Como tal, el problema de encontrar una buena clave de caché se vuelve más complicado. Dado que todas estas representaciones comparten el mismo URI, los cachés deben tener una manera de distinguirlos para servir la representación correcta en cada cliente, honrando la negociación de contenido.
Y dado que solo los servidores de origen saben qué diferentes representaciones están disponibles, nuevamente es responsabilidad del servidor de origen indicar a un caché en función de qué encabezados generará una representación diferente. Para ello, los servidores de origen deben añadir un Vary cabecera que contiene el valor de las cabeceras de petición que causan diferentes representaciones que se generen.
Cuando las memorias caché ven una respuesta proveniente de un servidor de origen con, por ejemplo, el encabezado Vary : Accept-Language, examinará el valor del encabezado Accept-Language, como fr-FR , y usará este valor para construir un más específico clave de caché, tal vez como https://example.net/home.html_ fr-FR .
La estrategia de implementación real es de poca importancia para nosotros. Alterar la clave de caché podría no ser la mejor manera de hacerlo. De alguna manera tiene que usar el valor del encabezado para diferenciar las representaciones.
El encabezado Vary puede apuntar a más de un encabezado, cuando los recursos están disponibles en varias representaciones. Seleccionar una clave de caché cuando hay varios encabezados involucrados no es realmente mucho más complicado que con solo un encabezado. El verdadero problema al variar en múltiples dimensiones es la explosión combinatoria .
Desafortunadamente, no hay formas de evitar esto. Si va a almacenar en caché y servir sus recursos en múltiples representaciones, debe pagar el costo de un gran almacenamiento. Si decide reducir su cardinalidad variable, algunos de sus usuarios recibirán aciertos de caché para las respuestas que no coincidan con sus solicitudes.
Por otro lado, si varía correctamente en todo y no tiene suficiente espacio de almacenamiento, es probable que sus usuarios no vean aciertos de caché en el corto plazo.
Ahora, es importante saber que esto solo es un problema si decide usar una memoria caché pública, para la cual dos solicitudes diferentes de dos usuarios diferentes ejecutan el mismo código, en el nivel de proxy. Si decide aprovechar solo la memoria caché del navegador, puede omitir el encabezado Vary por completo y servir los recursos en todas las representaciones que desee. Esto se debe a que el caché de cada navegador solo almacenará en caché las representaciones que coincidan con las preferencias del usuario. ¡Estas son buenas noticias!
Pero no nos adelantemos todavía. Como dijimos, los cachés usan el valor del encabezado como su entrada para generar una clave de caché más específica. Pero, ¿qué quiere decir que todos estos valores están bien formateados? ¡Absolutamente nada! Esta es la consecuencia de un inconveniente RFC padre ‘s principio de robustez . Los servidores HTTP son de hecho muy liberales en lo que aceptan .
Sin embargo hay esperanza.
Teniendo en cuenta el caso de un servidor de origen que solo puede producir una representación en dos idiomas diferentes, las memorias caché deben poder reagrupar los valores entrantes de aceptar contenido como fr, fr-FR, fr_FR ... en algo como FR. De lo contrario, al igual que antes con la explosión combinatoria, el número de representaciones explotará, pero en este caso, por una razón equivocada.
El proceso por el cual todas estas representaciones se reagrupan se llama normalización y a menudo se realiza en la memoria caché. Muchos cachés ofrecen utilidades de configuración o sus propios idiomas para hacer frente a estas situaciones. A veces, las funciones incluso ya están escritas, o se pueden encontrar fragmentos fácilmente en Internet. Las siguientes imágenes ilustran el proceso para el infame encabezado User-Agent.
Rápidamente, un CDN popular, probó 100 000 solicitudes y descubrió que el encabezado Accept-Encoding se expresaba en 44 formas diferentes. En cuanto al encabezado User-Agent, encontraron un poco de ... ¡8000 diferentes! Sin normalización, es probable que el caché nunca vea ningún golpe.
Esto cierra la sección sobre variación de representación. En este punto, sabemos cómo instruir a los cachés para almacenar nuestros recursos, y hemos aprendido a aprovechar el encabezado Vary para evitar que ocurran accidentes al usar cachés públicos. Ahora hemos cubierto suficiente especificación para poder almacenar recursos en caché de manera efectiva.

Conceptos erróneos comunes

En este momento, debe tener una comprensión profunda de cómo funciona el almacenamiento en caché HTTP. El control de la frescura, las representaciones de los recursos y los éxitos de caché ya no son conceptos misteriosos para usted. Y si comienza a sentirse fortalecido por todo este conocimiento, tenemos buenas noticias para usted: hemos cubierto una gran parte de la especificación, y ahora sabe casi todo lo que es necesario para estar en funcionamiento.
Pero no te confundas. El almacenamiento en caché es un tema complejo.
La experiencia nos ha demostrado que, a menos que esté lidiando con eso a diario, lo que puede ser claro hoy se convertirá rápidamente en algo bastante borroso después de unas pocas semanas. Por lo tanto, decidimos concluir este segundo artículo disipando dos conceptos erróneos comunes que son demasiado fáciles de hacer.

Control de frescura y validación

Esto puede parecer obvio después de leer las secciones anteriores, pero vale la pena repetirlo muchas veces. El control y la validación de la frescura ( que hemos discutido un poco al principio ) son dos mecanismos muy distintos que sirven para dos propósitos muy diferentes e involucran solicitudes HTTP entre diferentes piezas.
  • El control de la frescura siempre ocurre en un caché y se basa únicamente en el tiempo.
  • Las validaciones siempre ocurren en el servidor de origen y se basan tanto en el tiempo como en los identificadores (ETags)
Esto es algo que nos parece importante recordarnos. Significa que una vez que el caché ha recibido instrucciones temporales, puede, y lo mejor cree, servirá recursos sin contactarse con el servidor de origen hasta que expire el temporizador.
Por ejemplo, si el archivo HTML de su aplicación web llega a un navegador y la respuesta HTTP incluye el encabezado Cache-Control: max-age = 86400, el navegador servirá la misma versión de su aplicación por un día. En este caso, el navegador lo serviría durante un día sin ninguna acción posible de usted o de nadie, excepto el usuario, si alguna vez decidiera vaciar el caché de su navegador.
Si está pensando que todos pueden cometer errores, y un día no es tan malo, prepárese: el valor máximo de la edad máxima es ... ¡31536000 segundos! Es decir, un año. Esta es la razón por la cual los archivos HTML son muy peligrosos para almacenar en caché de esta manera, y generalmente deben declararse con Cache-Control: no-cache.

Frescura y representación más reciente.

Otro concepto erróneo es creer que los éxitos de caché y la frescura tienen algo que ver con tener la última versión disponible de un recurso. Esto es lo que todos intentamos lograr, pero uno nunca puede saber realmente si el recurso al que se ha servido desde un caché es, de hecho, la versión más actualizada. De hecho, esto es válido incluso en ausencia de caché. Tiene que ver con la naturaleza de las aplicaciones distribuidas: las acciones de otras personas pueden cambiar las cosas con las que estamos interactuando en cualquier momento.
Al consultar el estado de la aplicación, el encabezado ETag siempre debe usarse para que el servidor sepa cuál es nuestra comprensión actual del estado de la aplicación. Y si no coincide con el servidor, se espera recibir 409 Conflict en el lado del cliente.