Si desarrollas sitios web, en algún momento de tu vida te has enfrentado a problemas derivados de la compatibilidad entre navegadores. Y si tuviera que preguntar por el navegador más problematico, sin duda la respuesta sería unánime: Internet Explorer. Cuándo creemos que con la siguiente versión del navegador de Microsoft olvidaremos todas las miserias que hemos sufrido con él, nos sorprende con nuevos obstáculos.
El último problema que me he encontrado con Internet Explorer no es nuevo, pero merece la pena mencionarlo. Por lo menos está presente desde la versión 8, y está relacionado en cómo cachea las peticiones Ajax con el método GET.
Al visitar un sitio web, el navegador guarda en caché los recursos estáticos (imágenes, archivos JavaScript y CSS) para ahorrar ancho de banda en sucesivas visitas y así disminuir el tiempo de carga. Con el contenido dinámico ocurre lo mismo: una petición GET Ajax es almacenada en caché de igual manera que un recurso estático. El navegador obtendrá ese contenido dinámico de la caché hasta que el servidor notifique que ha caducado y se necesite descargar de nuevo.
Pero Internet Explorer, por supuesto, no sigue estas reglas.
Internet Explorer guarda en caché una petición GET Ajax todo el tiempo necesario y más. Si el servidor no devuelve las cabeceras HTTP de caché correctas, ese tiempo es incierto. Si la cabecera Expires es enviada, IE no actualizará el contenido hasta que éste haya expirado, incluso si se fuerza el refresco de la página con Ctrl+F5.
Las soluciones
Si tenemos control sobre el servidor que suministra los recursos dinámicos, establecer las cabeceras HTTP que permitan cachear las peticiones (como añadir Cache-Control: no-cache
) será de gran ayuda.
Si no podemos modificar las cabeceras que envía el servidor con el contenido dinámico (como añadir Cache-Control: no-cache
), tenemos estas soluciones en la parte del cliente:
Peticiones POST
Las peticiones POST no son cacheadas por el navegador, a no ser que la respuesta incluya las cabeceras Cache-Control
or Expires
. Pero utilizar este método en lugar de GET para evitar la caché es una mala práctica: él método POST es para peticiones que modifican el estado del servidor y no para únicamente obtener datos.
Cache Buster
La técnica cache buster (o cache breaker) consite en añadir a la URL de la petición un parámetro adicional, para que el navegador crea que es una petición a un recurso nuevo y forzando así su descarga.
https://example.org/books/?id=lotr&_cb=123456
Cada vez que se necesite actualizar ese contenido para IE, basta con cambiar el valor del parámetro de cache buster. También se puede utilizar un valor aleatorio, con el inconveniente que ese recurso será siempre descargado por el usuario.
Si utilizas jQuery o zepto para las peticiones GET Ajax, ten en cuenta que por defecto guardan en caché todas esas peticiones. Para desactivarlo has de pasar la opción cache
como false
:
$.ajax({ url: "http://path.to/api/resource", cache: false }) .done(function(data){ console.log(data); });
Otras librerías como Sencha o MooTools tienen opciones similares en sus métodos Ajax. Si optas por utilizar el objeto XMLHttpRequest, sencillamente añade el parámetro a la URL.
var url = 'http://path.to/api/resource', params = '?_cb=' + Date.now(), xhr = new XMLHttpRequest(); xhr.open('GET', url+params, true); xhr.onreadystatechange = function() { if(xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } }; xhr.send();
(Date.now()
devuelve el número de milisegundos transcurridos desde el tiempo Unix, por lo que es una opción válida para generar un número al azar. Si necesitas dar soporte a Interner Explorer 8 o inferior debes utilizar en su lugar (new Date()).getTime()
.)
Hola Raúl, con respecto a la técnica del caché buster tengo dos preguntas que hacerte.
Dices:
-«…para que el navegador crea que es una petición a un recurso nuevo y forzando así su descarga.»-
¿Te refieres a la descarga del resultado de la petición que sería considerada como datos nuevos y por tanto dejarían obsoletos los anteriormente conseguidos? Y si es así, ¿No estaríamos dejando igualmente datos en la caché?
Más adelante:
-«También se puede utilizar un valor aleatorio, con el inconveniente que ese recurso será siempre descargado por el usuario.»-
¿Acaso no te estás refiriendo a lo mismo que te he señalado anteriormente?
Si todo esto es como creo; que termina siempre con un almacenamiento en caché no termino de entender la funcionalidad del caché buster y la solución sólo la veo efectiva en la modificación del encabezado.
Gracias por adelantado y por tu artículo.
Hola Javier, gracias por tu comentario.
Respecto a tu primera pregunta, el navegador siempre guarda en la caché cualquier recurso que se descargue de una web (a no ser que se tenga deshabilitada la opción, claro). Y como bien dices, con un cache buster actualizamos la versión almacenada en la cache.
Respecto a tu segunda pregunta, piensa en la técnica del cache buster como si se cambiara el nombre del recurso; si el navegador no encuentra en la caché un recurso con ese nombre, lo descargará del servidor.
Un ejemplo: un sitio web utiliza una tipografía para iconos, por lo que el navegador descarga el archivo /icons.woff2. Si se actualiza el archivo pero las cabeceras indican que el recurso en la caché no ha caducado todavía, el navegador no descargará la nueva versión.
Pero con un parámetro cache buster sí lo hará: /icons.woff2?v=1, y el navegador guardará la nueva entrada en la caché. Pero si el archivo se vuelve a actualizar y no cambiamos ese parámetro cache buster, el navegador utilizará la versión cacheada. Así que cambiar de nuevo el parámetro forzará su descarga: /icons.woff2?v=2.