Tutorial Cache Web: cómo gestionar el cacheo de nuestros contenidos

El concepto de cache (sin acento, a no confundir con la palabra caché que significa distinción o elegancia) es ampliamente utilizado en todos los campos de la informática. De manera general podríamos definirlo como el proceso en el que un conjunto de datos son duplicados con el fin de reducir el tiempo de acceso a la información original y optimizar el rendimiento de un sistema.

En el terreno web, el cacheo es el proceso de almacenamiento de documentos web (una página html, una imagen, una librería javascript, etc) con el objetivo de reducir el ancho de banda consumido por los visitantes, la carga de los servidores que atienden peticiones web y el retardo de una descarga. Una cache web almacena una copia de los documentos que son devueltos por un servidor de de tal forma que el mismo se encarga de atender las peticiones siguientes.

A la hora de definir una web en la que se espera un tráfico elevado cobra especial relevancia establecer una política de cacheo que permita crear un sistema escalable y evitar problemas a la hora de atender las peticiones de nuestros usuarios.

Existen tres tipos de caches que pueden actuar durante el proceso de solicitud de un documento web:

  1. Cache de Agente de Usuario (User-Agent): Está presenten en los navegadores web y lógicamente sólo funcionan para un único usuario.
  2. Cache Compartida o proxy-cachés directos: Este tipo de cache son utilizados por los proveedores de servicios de Internet y empresas para ahorrar ancho de banda. La comparten todos los usuarios que accedan.
  3. Cache pasarela o proxy-cachés inversos: Funcionan como respaldo de un servidor web de tal forma transparente para los usuarios. Es posible trabajar con varias cache pasarela de manera conjunta para implementar una Content Delivery Network (CDN) (pjem: Akami)

Tipo de Cache
Si planificamos correctamente un Site, la cache nos ayudará a mejorar el tiempo de carga y a gestionar de manera eficiente el ancho de banda de nuestro servidor. El incremento de rendimiento puede ser excepcional y la experiencia de nuestros visitantes mejorará notablemente sin necesidad de invertir ni un sólo euro.

Cómo se gestiona una petición web

Para comprender el proceso de cacheo es indispensable entender qué ocurre durante la carga de una web. El siguiente gráfico muestra como se comunican un navegador y un servidor web durante la solicitud y envío de una página no cacheada:

1. Navegador: Hola!, ¿podrías mandarme el archivo ejemplo.htm?

2. Servidor: Claro, espera un momento que lo busco en mi disco duro.

3. Servidor: Aquí esta!

4. Servidor: Te lo mando chico, cuidado que es un poco pesada 100kb y mi ancho de banda es limitado.

5. Navegador: Mucha gracias ya lo estoy descargando y mostrándoselo al usuario.

Si la página se encuentra cacheada el proceso podría ser similar el siguiente:

1. Navegador: Hola!, ¿podrías mandarme el archivo ejemplo.htm?

2. Servidor: Claro, espera un momento estoy comprobando la fecha de modificación del archivo.

3. Servidor: Aquí está!, y veo que no se ha modificado el fichero desde la última vez que lo solicitaste.

4. Servidor: Estás de suerte, la versión que tienes en tu cache es la última.

5. Navegador: Genial, entonces no hace falta que me la envíes ya estoy mostrando al usuario la página que tengo cacheada.

Qué métodos de cacheo existen.

Existen tres tipos de mecanismos que permiten gestionar el cacheo de un contenido:

1. Mecanismos por Validación (validation): En este mecanismo el servidor comprueba si la respuesta que mantiene cacheada el navegador sigue siendo válida. Existen dos variantes:

Last-Modified

Cuando el servidor devuelve un documento por primera vez a un navegador adjunta también la fecha de modificación del fichero (Last-modified):

<meta http-equiv="last-modified" content="Fri, 14 Dec 2007 12:58:00 GMT" />

La próxima vez que el navegador solicite el documento, el servidor enviará como respuesta el mensaje «Not Modified» en el caso de que el documento no haya sido modificado desde su última petición. El navegador mostrará entonces al usuario el documento que tiene almacenado en su cache.

ETag (Entity Tag)

El método Last-Modified no es infalible y puede presentar problemas si existen desajustes en el reloj interno del servidor Web.

ETag es un identificador único (un hash MD5) que se identifíca de manera única cada fichero cada vez que se crea o modifica. De esta manera, en vez de comprobar la fecha de modificación se chequea su ETag para conocer si ha cambiado respecto a la versión cacheada:

ETag: h3110g00g13

Contenido del fichero solicitado.

Más adelante podrá ver como realizar se puede realizar esta comprobación en el servidor.

2. Mecanismos por Frescura (freshness): Tanto el método Last-Modified como ETag requieren que el navegador se comunique con el servidor para comprobar la versión del fichero. En los mecanismos por frescura en cambio cada repuesta lleva asociada una fecha de caducidad (como un yogurt) y puede ser utilizada sin necesidad de que el servidor compruebe su validez. Existen dos formas de implementar este mecanismo:

Expires.

El Método Expires consiste en asignar una fecha de caducidad al fichero, el navegador no solicita al servidor una versión nueva hasta que no se traspasa la fecha de expiración del archivo.

<meta http-equiv="Expires" content="Mon, 14 Dec 2007 12:58:00 GMT">

De esta forma la comunicación se reduce a un monólogo entre el navegador que no solicita una nueva versión al servidor mientras no se exceda el periodo de expiración del fichero.

Max-age

Es un método similar a Expires con la particularidad de que la fecha de caducidad del documento se establece de manera relativa, es decir, «Este documento expirara dentro de x segundos a partir de hoy».

<meta http-equiv="Cache-Control" content="max-age=3600«>

Recuerde que el tiempo se debe establecer en segundos, un año por ejemplo serían 31.536.000 de segundos.

3. Mecanismo por Invalidación: Este mecanismo se deriva de otra petición que pasa por la caché. Por ejemplo, si la url asociada con una respuesta cacheada es solicitada más tarde a través de una petición POST, PUT o DELETE, la respuesta que se encontraba cacheada quedará invalidada. Esto es lo que ocurre por ejemplo cuando intenta navegar hacia atrás en su historial tras haber enviado los datos de un formulario.

Además tenga en cuenta que existen una serie de reglas que también aplican al cacheo de contenidos y deben tenerse en cuenta:

  • Bajo determinadas circunstancias, como por ejemplo, cuando un equipo se desconecta de la red, la cache puede servir páginas sin consultar con el servidor de origen.
  • Un documento nunca se cachea si la cabecera del documento indica de manera explicita que no es cachee.
  • Si se utiliza un protocolo seguro ((HTTPS) la página nunca será cacheada.

Cómo evitar el cacheo de nuestros contenidos

En ocasiones el cacheo de contenidos puede interferir con el correcto funcionamiento de la web y por tanto debemos evitarlo. El funcionamiento de la cache se puede controlar con las siguientes directivas:

  • Cache-control: max-age – Especifica el número máximos de segundos en los que el contenido sera considerado como fresco
  • Cache-control: s-maxage – Similar a la directiva max-age, pero aplicable solo para caches compartidas (pejm: un proxy).
  • Cache-control: public – indica que la versión cacheada puede ser guardada por proxies y otros servidores intermedios para que todo el mundo tenga acceso a ella..
  • Cache-control: private – indica que el archivo no es el mismo para usuarios diferentes. De esta manera el archivo puede ser cacheado por el navegador del usuario pero no debe ser cacheado por proxies intermedios.
  • Cache-control: no-cache – Significa que el archivo no debe ser cacheado, esto puede ser necesario en casos en los que una misma url pueda devolver diferentes contenidos.
  • Cache-control: no-store – Indica al navegador que sólo guarde el documento el tiempo necesario para mostrarlo. Le recomiendo la lectura del siguiente artículo dónde se alerta sobre el peligro de utilizar la directiva no-store en situaciones no adecuadas.
  • Cache-control: must-revalidate – Indica a la cache que deben hacer caso a cualquier directiva de cacheo que le indiquemos. Tenga en cuenta que la especificación HTTP permite a las caches atender de manera automática a las peticiones bajo determinadas circustancias. ¨La directiva must-revalidete obliga a la cache a seguir nuestras directivas de manera estricta. La forma de utilizarla es la siguiente:

    <meta http-equiv="Cache-Control" content="max-age=3600, must-revalidate«>

  • Cache-control: proxy-revalidate – Similar a must-revalidate pero sólo aplicable a proxy caches.

Como ve no existe una única forma que indicar que una página no sea cacehada por un navegador. Generalmente se utiliza la siguiente cabecera:

<meta http-equiv="Cache-Control" content="max-age=0, no-cache, no-store, private">

<meta http-equiv="Pragma" content="nocache">

La directiva Pragma tiene el mismo significado que Cache-control: no-cache y se suele incluir para asegurarnos la compatibilidad con versiones anteriores a HTTP/1.0. ( Debe tener en cuenta que alguna de estas directivas sólo funcionan con las navegadores modernos.)

En lugar de utilizar el tag meta también puede crear las cabeceras HTTP para sus documentos con cualquier lenguajes de scripting de servidor: PHP, ASP, .NET, etc. Recuerde que cualquier directivas de cacheo deben incluirse al principio del documento, incluso antes del tag html. Aquí le muestro algunos códigos de ejemplo:

php:

header("Cache-Control:...");

header("Pragma:...");

Cold Fusion

<CFHEADER NAME="Expires" VALUE="...">

ASP

<% Response.CacheControl="..." %>

ASP. net

Response.Cache.SetExpires (... )

Tipo de páginas en función de su cacheabilidad

Una web puede ofrecer tres tipos de contenidos en función de su cacheabilidad.

  1. Contenidos estáticos: Son aquellos contenidos que ya existen físicamente en el servidor, tienen un tamaño fijo y una fecha determinada. Una página html, una imagen o un documento pdf son ejemplos de contenidos estáticos. Este tipo de contenidos son firmes candidatos a ser cacheados.
  2. Contenidos dinámicos: Son contenidos que se crean en el momento en el que se solicitan accediendo, por ejemplo, a una base de datos. Este tipo de páginas son independientes del perfil del usuario que las visita y por tanto son potencialmente cacheables.
  3. Contenidos personalizados: Este tipo de contenidos se crean a medida en base a las características de la petición y pueden ser diferentes en función de quién, cuándo, o cómo es solicitada. Por este motivo este tipo de contenidos no son buenos candidatos a ser cacheados, aunque como veremos más adelante hay técnicas que permiten cachearlos de manera parcial o total. El área privada de la web de un banco, o un carrito de la compra son dos ejemplos de páginas personalizadas.

Es muy importante que entendamos qué contenidos pueden y deben cachearse y cuales no. Si no gestionamos eficientemente el cacheo corremos el riesgo de servir a nuestros usuarios un contenido que no está actualizado o por otro lado saturar nuestro servidor con peticiones innecesarias.

Estrategias de cacheo

Para optimizar la eficiencia del cacheo de contenidos tanto en el navegador como en los servidores proxy existen una serie de estrategias que podemos tener en cuenta:

1. Evite utilizar Query String

Utilizar Friendly URLs en lugar de query strings facilita el cacheo de los contenidos. Algunos navegadores y muchos proxies cache no cachean contidos que lleven una query string en la URL.

Recuerde que puede utilizar mod_rewrite en Apache Web Server para implementar friendly urls.

2. Organice eficientemente sus contenidos

Puede organizar sus contenidos en cacheables y no cacheables, ubicándolos en diferentes rutas dentro del servidor.

Por ejemplo es una práctica habitual ubicar los contenidos no cacheables de una web en /cgi-bin/ y configurar el servidor para que informe a los navegadores con las cabeceras HTTP necesarias de que este contenido no sea cacheado.

3. Envíe la cabecera Last-Modified

Incluya en sus documentos la información Last-Modified. De esta manera los navegadores podrán hacer peticiones condicionales del tipo if-Modified-Since (IMS) para que el servidor le entregue el contenido sólo en el caso de que su cache haya caducado.

Para las páginas estáticas no existen ningún problema ya que existen físicamente el el servidor y tienen un time stamp asociado, pero en el caso una página dinámica donde el contenido no existe fisicamente, es la propia aplicación la que debe preguntar si el contenido ha cambiado.

Una gestión eficiente de las solicitudes condicionales tiene una repercusión muy positiva en el ahorro de ancho de banda del servidor web.

Estratégias de cacheo para páginas personalizadas

Aquellas contenidos que se deben mostrar de manera personalizada para cada usuario también pueden ser objeto de cacheo si tenemos en cuenta las siguientesconsideraciones:

1. Evite incluir fragmentos dependientes de la sesión HTTP del usuario

Cuando un documento web se crea dinámicamente en el servidor web, evite que alguno de los elementos que componen la página dependa de la sesión HTTP del usuario. De esta manera conseguirá optimizar el cacheo de cada elemento y se podrá integrar más fácilmente en sistemas externos como Akamai que proporcionan gran escalabilidad.

2.Gestione eficiente la información en Cookies en el navegador

Las cookies permiten almacenar de manera local la información personalizable de casa usuario de tal manera que el resto de contenidos de la página pueden ser objeto de cacheo.

Por ejemplo, si queremos mostrar un mensaje de bienvenida personalizado podríamos almacenar este mensaje un una cookie local de tal manera que el resto del contenido pudiera cachearse.

3. Muestre el perfil del usuario en la URL

Podemos recurrir a técnicas de URL rewriting para mostrar en la url elementos que indiquen el país o el tipo de usuario. Por ejemplo:

http://www.hellogoogle.com/es/home-user/welcome/

Luego por técnicas de url rewriting podrían convertirse en una url dinámica del tipo:

http://www.hellogoogle.com/welcome/?country=es&usertype=home-user

Como ya vimos este tipo de friendly urls pueden ser cacheadas por los navegadores de los usuarios.

4. Gestione de manera independiente la información personalizada

Técnicas como AJAX o Adobe Flex nos permiten mostrar al usuario información personalizada mediante peticiones GET o POST una vez cargada la página. Esto nos abre la posibilidad de cachear el documento html sin que ello interfiera con la información que se muestra de manera personalizada a cada usuario.

5. Utilice la cabecera ETAG para gestionar el cacheo de contenidos dinámicos

Como ya vimos anteriormente ETAG (Entity Tag) es un sistema que permite identificar a un contenido de manera unívoca, Los servidores Web pueden realizar validaciones de contenido basándose en esta información al igual que hacen con la información Last-Modified.

Obtener un ETAG a partir del contenido dinámico es sencillo y pemite minimizar el tiempo de respuesta en las revalidaciones de estos contenidos.

Dentro del ciclo de vida de la petición dentro del servidor, tras la generación del contenido, la aplicación puede obtener una hash MD5. La MD5 sería el ETAG que representa a esa petición. Si es igual a la ETAG enviada por el navegador, se indica al usuario final que el contenido no ha cambiado para que la cargue de su cache local (HTTP/1.x 304)::

var contenido = response.buffer

var path = request.header("PATH")

var ETAG = funcion-MD5 (path, contenido) //Obtenemos el ETAG de la petición

var request-ETAG = request.header("if-none-match") //Obtenemos el ETAG del navegador

Si ETAG = request-ETAG Entonces //Comparamos ambos ETAG

response.status = 304 //Si son iguales el contenido no ha variado.

response.buffer = ''

return

Si No

response.addheader("ETAG", ETAG)

Fin Si

Utilizar ETAG junto con las directrices de Cache Cache-Control o Expires, permite ahorrar peticiones innecesarias y ancho de banda de nuestro servidor.

La cache universal: la solución a todos nuestros males

Como hemos podido ver en este artículo de hellogoogle.com la gestión eficiente de la cache es fundamental en el desarrollo y mantenimiento de un Site. Si puliéramos trabajar con una cache nuestra vida sería mucho más sencilla: Por ejemplo, no  haría falta pedir cita con el  médico, éste quedaría cacheado tras la primera visita del día y cada ciudadano tendríamos un médico en cache a nuestra disposición las 24h del día. Tampoco tendríamos que hacer cola en el autobús o en el cine: una vez que hubiera accedido el primer usuario el resto tendría un autobús y un cine cacheado al instante.

Me temo que aún queda mucho tiempo para que la ciencia ponga a nuestra disposición un mundo cacheado en el que todos los seres humanos podamos vivir en paz sin necesidad de competir por un puesto de trabajo, una mujer o un trozo de pastel de chocolate. Mientras tanto seguiremos enfrentándonos a la cruda realidad de un mundo con recursos limitados, guerras, muerte y enfermedad.

¿Quien sabe?, después de todo quiza no sea tan malo tener que esperar un ratito en la cola del dentista, yo al menos, no tengo ninguna prisa.