Cache busting a través de params

123

Queremos almacenar en caché los desplomes de producción, pero no perder un montón de tiempo pensando en un sistema para hacerlo. Mi pensamiento era aplicar un parámetro al final de los archivos css y js con el número de versión actual:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

Dos preguntas: ¿Romperá efectivamente la memoria caché? ¿El parámetro hará que el navegador nunca almacene en caché la respuesta de esa URL ya que el parámetro indica que se trata de contenido dinámico?

Brad Herman
fuente

Respuestas:

115

El parámetro ?v=1.123indica una cadena de consulta, por lo que el navegador va a pensar que es un nuevo camino de, por ejemplo, ?v=1.0. Por lo tanto, hace que se cargue desde el archivo, no desde la caché. Como quieras.

Y el navegador asumirá que la fuente permanecerá igual la próxima vez que llame ?v=1.123y debería almacenarla en caché con esa cadena. Por lo tanto, permanecerá en caché, independientemente de cómo esté configurado su servidor, hasta que se mueva a ?v=1.124o así sucesivamente.

Marshall
fuente
4
Citando a Steve Souders: "Para obtener el beneficio del almacenamiento en caché mediante proxies populares, evite acelerar con una cadena de consulta y, en su lugar, acelere el nombre del archivo". La explicación completa se puede encontrar aquí: stevesouders.com/blog/2008/08/23/…
lao
25
Esa publicación de blog se acerca a una década. ¿Cree que los proveedores de caché y las CDN todavía tienen que adaptarse? Squid parece poder almacenar en caché documentos con cadenas de consulta ahora .
jeteon
1
Tal vez esto ayude a alguien: Personalmente, uso la marca de tiempo de modificación del archivo como un parámetro de versión 'automático', por ejemplo. <link rel="stylesheet" href="style.css?v=1487935578" />
oelna
Personalmente no entiendo por qué, pero Lara Hogan (Swanson) (gerente de ingeniería en Etsy) no recomienda el uso de parámetros de consulta para el almacenamiento en caché. Creo que tiene que ver con los proxies de caché entre el usuario y el servidor.
Sam Rueby
36

Dos preguntas: ¿Romperá efectivamente la memoria caché?

Si. Incluso Stack Overflow usa este método, aunque recuerdo que ellos (con sus millones de visitantes por día y miles de millones de diferentes versiones y configuraciones de cliente y proxy) han tenido algunos casos extremos en los que ni siquiera esto fue suficiente para romper el caché. Pero la suposición general es que esto funcionará y es un método adecuado para romper el almacenamiento en caché en los clientes.

¿El parámetro hará que el navegador nunca almacene en caché la respuesta de esa URL ya que el parámetro indica que se trata de contenido dinámico?

No. El parámetro no cambiará la política de almacenamiento en caché; los encabezados de almacenamiento en caché enviados por el servidor aún se aplican, y si no envía ninguno, los valores predeterminados del navegador.

Pekka
fuente
1
@spender No puedo encontrar la referencia en este momento, me temo, hubo un extenso artículo de blog o una respuesta SO en la que Jeff Atwood habla de ello (IIRC)
Pekka
2
@spender He leído que algunos servidores proxy (ya sean antiguos o se pueden configurar para) ignoran la cadena de consulta al almacenar en caché.
MrWhite
2
@spender: he escuchado lo mismo y creo que cambiar el nombre del archivo o la ruta es la mejor opción. Podría ser más fácil simplemente dejar mover todos sus archivos estáticos bajo un nombre de carpeta versionado, por ejemplo /static/v22/file.css, ya que podría hacer varios archivos con un solo cambio de nombre de carpeta, por ejemplo /static/v23/file.cssy/static/v23/mystuff.js
Brad Parks
22

Es más seguro poner el número de versión en el nombre del archivo real. Esto permite que existan varias versiones a la vez para que pueda implementar una nueva versión y, si aún existen páginas HTML almacenadas en caché que solicitan la versión anterior, obtendrán la versión que funciona con su HTML.

Tenga en cuenta que en una de las implementaciones con versiones más grandes de Internet, jQuery usa números de versión en el nombre de archivo real y permite de manera segura que coexistan múltiples versiones sin ninguna lógica especial del lado del servidor (cada versión es solo un archivo diferente).

Esto rompe el caché una vez cuando implementa nuevas páginas y nuevos archivos vinculados (que es lo que desea) y, a partir de ese momento, esas versiones se pueden almacenar en caché de manera efectiva (lo que también desea).

jfriend00
fuente
Estoy de acuerdo con eso, pero es mucho más fácil que Sinatra agregue? V = <% = VERSION%> a todas las solicitudes css y js en lugar de tener que controlar cada archivo individualmente. Con el tiempo, cambiaremos a sinatra-assetpack, que preprocesará y comprimirá todos los archivos y, de hecho, agregará un número de versión al nombre del archivo, lo que nos permitirá controlarlos individualmente de forma mucho más sencilla.
Brad Herman
1
Estoy de acuerdo en que poner el número de versión en el nombre del archivo es la solución más segura si quieres estar 10000% seguro, pero no sigo el argumento de que "existen múltiples versiones a la vez". Una URL con un parámetro de consulta es distinta de la misma URL con un parámetro de consulta diferente. El cliente debe tratarlos como dos recursos diferentes; si no es así, el cliente está roto.
Pekka
2
@Pekka: el número de versión puede permitir que existan varias versiones a la vez, pero eso requiere la cooperación del servidor para asignar el parámetro de consulta al archivo real correcto. No creo que eso sea lo que está haciendo el OP aquí y hay pocas razones para requerir esa complicación cuando modificar el nombre del archivo es mucho más simple y no necesita la cooperación del servidor. Obviamente ambos pueden funcionar.
jfriend00
11

Como han dicho otros, la destrucción de caché con un parámetro de consulta generalmente se considera una mala idea (tm) y lo ha sido durante mucho tiempo. Es mejor reflejar la versión en el nombre del archivo. Html5 Boilerplate recomienda no usar la cadena de consulta, entre otros.

Dicho esto, de las recomendaciones que he visto que citaban una fuente, todas parecen tomar su sabiduría de un artículo de 2008 de Steve Souders. Sus conclusiones se basan en el comportamiento de los apoderados en ese momento, y pueden ser relevantes o no en estos días. Aún así, en ausencia de información más actual, cambiar el nombre del archivo es la opción segura.

hashchange
fuente
9

Romperá la caché una vez, después de que el cliente haya descargado el recurso, todas las demás respuestas se entregarán desde la caché del cliente a menos que:

  1. se actualiza el parámetro v.
  2. el cliente borra su caché
ncremins
fuente
6

En general, esto debería estar bien, pero es posible que esto no funcione si hay una caché intermedia (un proxy) que está configurada para ignorar los parámetros de la solicitud.

Por ejemplo, si está sirviendo contenido estático a través de Akamai CDN, se puede configurar para ignorar los parámetros de solicitud y evitar la rotura de la memoria caché con este método.

Ken Liu
fuente
5

Depende en gran medida de lo robusto que desee que sea su almacenamiento en caché. Por ejemplo, el servidor proxy squid (y posiblemente otros) por defecto a no almacenar en caché URL servido con una cadena de consulta - al menos, lo hizo cuando ese artículo fue escrito. Si no le importan ciertos casos de uso que causan pérdidas de caché innecesarias, continúe con los parámetros de consulta. Pero es muy fácil configurar un esquema de eliminación de caché basado en el nombre de archivo que evita este problema.

Bobby Jack
fuente
5
El proxy squid que se citó en el artículo de Steve Souders ha cambiado su política de almacenamiento en caché predeterminada. Desde la versión 2.7 (mayo de 2008) y la versión 3.1 (marzo de 2010), el comportamiento predeterminado es almacenar en caché el contenido dinámico.
Josh Rack
5

Encontré una comparación de las 2 técnicas (cadena de consulta vs nombre de archivo) aquí :

La versión como cadena de consulta tiene dos problemas.

En primer lugar, puede que no siempre sea un navegador que implemente el almacenamiento en caché a través del cual necesitamos pasar. Se dice que ciertos proxies (posiblemente más antiguos) ignoran la cadena de consulta con respecto a su comportamiento de almacenamiento en caché.

En segundo lugar, en ciertos escenarios de implementación más complejos, donde tiene múltiples servidores frontend y / o backend, una actualización es cualquier cosa menos instantánea. Debe poder servir tanto la versión antigua como la nueva de sus activos al mismo tiempo. Vea, por ejemplo, cómo le afecta esto al utilizar Google App Engine.

usuario
fuente
4

Otro enfoque similar es usar htaccess mod_rewrite para ignorar parte de la ruta al entregar los archivos. Su página de índice nunca almacenada en caché hace referencia a la ruta más reciente a los archivos.

Desde una perspectiva de desarrollo, es tan fácil como usar parámetros para el número de versión, pero es tan robusto como el enfoque del nombre de archivo.

Use la parte ignorada de la ruta para el número de versión, y el servidor simplemente lo ignorará y entregará el archivo sin caché.

1.2.3/css/styles.csssirve el mismo archivo que css/styles.cssdesde que el primer directorio es eliminado e ignorado por el archivo htaccess

Incluyendo archivos versionados

<?php
  $version = "1.2.3";
?>

<html>
  <head>
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
  </head>
  <body>
    <script src="<?php echo $version ?>/js/main.js"></script>
  </body>
</html>

Tenga en cuenta que este enfoque significa que debe deshabilitar el almacenamiento en caché de su página de índice: ¿ usa etiquetas <meta> para desactivar el almacenamiento en caché en todos los navegadores?

archivo .htaccess

RewriteEngine On

# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f 
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d 

# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L] 

Puede adoptar el mismo enfoque en cualquier plataforma de servidor que permita la reescritura de URL

(condición de reescritura adaptada de mod_rewrite - reescribir el directorio en la cadena de consulta excepto / #! / )

... y si necesita eliminar la memoria caché para su página de índice / punto de entrada del sitio, siempre puede usar JavaSript para actualizarlo.

alexanderbird
fuente
2
<script type="text/javascript">
// front end cache bust

var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
</script> 
Conete Cristian
fuente
Durante el desarrollo / prueba de nuevas versiones, la caché puede ser un problema porque el navegador, el servidor e incluso a veces la telco 3G (si realiza la implementación móvil) almacenará en caché el contenido estático (por ejemplo, JS, CSS, HTML, img). Puede superar esto agregando un número de versión, un número aleatorio o una marca de tiempo a la URL, por ejemplo: JSP: <script src = "js / excel.js? Time = <% = new java.util.Date ()%>"> </ script> En caso de que esté ejecutando HTML puro (en lugar de páginas de servidor JSP, ASP, PHP), el servidor no le ayudará. En el navegador, los enlaces se cargan antes de que se ejecute JS, por lo tanto, debe eliminar los enlaces y cargarlos con JS
Conete Cristian
No creo que esto cargue los archivos JS en orden, sincrónicamente.
Stealth Rabbi
0
 <script>
    var storedSrcElements = [
         "js/exampleFile.js",
         "js/sampleFile.js",
         "css/style.css"
          ];

    var head= document.getElementsByTagName('head')[0];
    var script;
    var link;
    var versionNumberNew = 4.6;

    for(i=0;i<storedSrcElements.length;i++){
     script= document.createElement('script');
     script.type= 'text/javascript';
     script.src= storedSrcElements[i] + "?" + versionNumberNew;
     head.appendChild(script);
    }     


     </script> 


       ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###
Teja
fuente
0

Espero que esto te ayude a inyectar un archivo JS externo

<script type="text/javascript"> 
var cachebuster = Math.round(new Date().getTime() / 1000); 
document.write('<scr'+'ipt type="text/javascript" src="external.js?cb=' +cachebuster+'"></scr' + 'ipt>');
</script>

Fuente: código Cachebuster en JavaScript

Vinit Kadkol
fuente