¿Cómo obligar a los navegadores a recargar archivos CSS y JS almacenados en caché?

Resuelto Peter Mortensen asked hace 16 años • 0 respuestas

He notado que algunos navegadores (en particular, Firefox y Opera ) son muy entusiastas en el uso de copias en caché de archivos .css y .js , incluso entre sesiones del navegador. Esto genera un problema cuando actualiza uno de estos archivos, pero el navegador del usuario sigue usando la copia en caché.

¿Cuál es la forma más elegante de obligar al navegador del usuario a recargar el archivo cuando ha cambiado?

Idealmente, la solución no obligaría al navegador a recargar el archivo en cada visita a la página.


La sugerencia de John Millikin y da5id me ha resultado útil. Resulta que existe un término para esto: versiones automáticas .

Publiqué una nueva respuesta a continuación que es una combinación de mi solución original y la sugerencia de John.

Otra idea sugerida por SCdF sería agregar una cadena de consulta falsa al archivo. ( Pi envió parte del código Python para usar automáticamente la marca de tiempo como una cadena de consulta falsa . .)

Sin embargo, existe cierta discusión sobre si el navegador almacenará en caché un archivo con una cadena de consulta. (Recuerde, queremos que el navegador almacene en caché el archivo y lo use en futuras visitas. Solo queremos que vuelva a buscar el archivo cuando haya cambiado).

Peter Mortensen avatar Sep 23 '08 10:09 Peter Mortensen
Aceptado

Esta solución está escrita en PHP, pero debería adaptarse fácilmente a otros lenguajes.

La expresión regular original .htaccesspuede causar problemas con archivos como json-1.3.js. La solución es reescribir sólo si hay exactamente 10 dígitos al final. (Porque 10 dígitos cubren todas las marcas de tiempo desde el 9/9/2001 hasta el 20/11/2286).

Primero, usamos la siguiente regla de reescritura en .htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Ahora, escribimos la siguiente función PHP:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *
 *  @param $file  The file to be loaded. works on all type of paths.
 */
function auto_version($file) {
  if($file[0] !== '/') {
    $file = rtrim(str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['PHP_SELF'])), '/') . '/' . $file;
  }
  
  if (!file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
  return $file;
  
  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Ahora, dondequiera que incluyas tu CSS, cámbialo de esto:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

A esto:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

De esta manera, nunca más tendrá que modificar la etiqueta del enlace y el usuario siempre verá el CSS más reciente. El navegador podrá almacenar en caché el archivo CSS, pero cuando realice cambios en su CSS, el navegador lo verá como una nueva URL, por lo que no utilizará la copia en caché.

Esto también puede funcionar con imágenes, favicons y JavaScript. Básicamente cualquier cosa que no se genere dinámicamente.

Kip avatar Sep 23 '2008 03:09 Kip

Técnica simple del lado del cliente

En general, el almacenamiento en caché es bueno... Por lo tanto, existen un par de técnicas, dependiendo de si está solucionando el problema usted mismo mientras desarrolla un sitio web o si está intentando controlar el caché en un entorno de producción.

Los visitantes generales de su sitio web no tendrán la misma experiencia que usted cuando desarrolla el sitio. Dado que el visitante promedio visita el sitio con menos frecuencia (tal vez solo unas pocas veces al mes, a menos que sea Google o Hi5 Networks), es menos probable que tenga sus archivos en caché, y eso puede ser suficiente.

Si desea forzar una nueva versión en el navegador, siempre puede agregar una cadena de consulta a la solicitud y aumentar el número de versión cuando realice cambios importantes:

<script src="/myJavascript.js?version=4"></script>

Esto asegurará que todos obtengan el nuevo archivo. Funciona porque el navegador mira la URL del archivo para determinar si tiene una copia en la caché. Si su servidor no está configurado para hacer nada con la cadena de consulta, se ignorará, pero el nombre aparecerá como un archivo nuevo en el navegador.

Por otro lado, si está desarrollando un sitio web, no querrá cambiar el número de versión cada vez que guarde un cambio en su versión de desarrollo. Eso sería tedioso.

Entonces, mientras desarrollas tu sitio, un buen truco sería generar automáticamente un parámetro de cadena de consulta:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

Agregar una cadena de consulta a la solicitud es una buena manera de versionar un recurso, pero para un sitio web simple esto puede resultar innecesario. Y recuerde, el almacenamiento en caché es algo bueno.

También vale la pena señalar que el navegador no es necesariamente tacaño a la hora de mantener archivos en caché. Los navegadores tienen políticas para este tipo de cosas y, por lo general, siguen las reglas establecidas en la especificación HTTP. Cuando un navegador realiza una solicitud a un servidor, parte de la respuesta es un encabezado Expires ... una fecha que le indica al navegador cuánto tiempo debe mantenerse en caché. La próxima vez que el navegador encuentre una solicitud para el mismo archivo, verá que tiene una copia en la caché y buscará la fecha de Caducidad para decidir si debe usarse.

So believe it or not, it's actually your server that is making that browser cache so persistent. You could adjust your server settings and change the Expires headers, but the little technique I've written above is probably a much simpler way for you to go about it. Since caching is good, you usually want to set that date far into the future (a "Far-future Expires Header"), and use the technique described above to force a change.

If you're interested in more information on HTTP or how these requests are made, a good book is "High Performance Web Sites" by Steve Souders. It's a very good introduction to the subject.

keparo avatar Sep 23 '2008 04:09 keparo