Bloqueio de cache via parâmetros

122

Queremos esconder o busto nas implantações de produção, mas não perder muito tempo tentando descobrir um sistema para isso. Meu pensamento foi aplicar um parâmetro ao final dos arquivos css e js com o número da versão atual:

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

Duas perguntas: isso efetivamente interromperá o cache? O parâmetro fará com que o navegador nunca armazene em cache a resposta desse URL, pois o parâmetro indica que esse é um conteúdo dinâmico?

Brad Herman
fonte

Respostas:

115

O parâmetro ?v=1.123indica uma string de consulta e, portanto, o navegador acha que é um novo caminho para, digamos ?v=1.0,. Fazendo com que ele seja carregado do arquivo, não do cache. Como você quiser.

E o navegador assumirá que a fonte permanecerá a mesma na próxima vez que você ligar ?v=1.123e deverá armazená-la em cache com essa sequência. Portanto, ele permanecerá armazenado em cache, no entanto, seu servidor estará configurado, até que você mude para mais ?v=1.124ou menos.

Marshall
fonte
4
Citando Steve Souders: "Para obter o benefício do armazenamento em cache por proxies populares, evite acelerar com uma string de consulta e, em vez disso, revele o próprio nome do arquivo". A explicação completa pode ser encontrada aqui: stevesouders.com/blog/2008/08/23/…
lao
25
Esse post do blog está se aproximando há uma década. Você acha que os provedores de cache e CDNs ainda precisam acomodá-lo? O Squid parece capaz de armazenar em cache documentos com seqüências de caracteres de consulta agora .
jeteon
1
Talvez isso ajude alguém: Pessoalmente, eu uso o registro de data e hora da modificação do arquivo como um parâmetro de versão 'automático', por exemplo. <link rel="stylesheet" href="style.css?v=1487935578" />
oelna
Pessoalmente, não entendo o motivo, mas Lara Hogan (Swanson) (gerente de engenharia da Etsy) não recomenda o uso de parâmetros de consulta para impedir o armazenamento em cache. Eu acho que tem a ver com proxies de cache entre o usuário e o servidor.
Sam Rueby
36

Duas perguntas: isso efetivamente interromperá o cache?

Sim. Até o Stack Overflow usa esse método, embora eu lembre-se de que eles (com milhões de visitantes por dia e zilhões de diferentes versões e configurações de clientes e proxy) tiveram alguns casos extremos onde até isso não foi suficiente para interromper o cache. Mas a suposição geral é que isso funcionará e é um método adequado para interromper o armazenamento em cache nos clientes.

O parâmetro fará com que o navegador nunca coloque em cache a resposta desse URL, pois o parâmetro indica que esse é um conteúdo dinâmico?

Não. O parâmetro não alterará a política de cache; os cabeçalhos de cache enviados pelo servidor ainda se aplicam e, se não enviar, os padrões do navegador.

Pekka
fonte
1
@spender eu não posso encontrar a referência agora eu estou com medo, havia um artigo de blog longa ou resposta SO onde fala Jeff Atwood sobre ele (IIRC)
Pekka
2
@ spender Eu li que alguns servidores proxy (antigos ou podem ser configurados para) ignoram a string de consulta durante o cache.
precisa saber é o seguinte
2
@ spender - ouvi o mesmo e acho que alterar o nome do arquivo ou o caminho é a melhor opção. Pode ser mais fácil de simplesmente deixar mover todos os seus arquivos estáticos em um nome de pasta de versão, por exemplo, /static/v22/file.csscomo você poderia fazer vários arquivos com um único renomear pasta, por exemplo, /static/v23/file.csse/static/v23/mystuff.js
Brad Parks
22

É mais seguro colocar o número da versão no nome do arquivo real. Isso permite que várias versões existam ao mesmo tempo, para que você possa lançar uma nova versão e, se ainda existirem páginas HTML em cache que solicitem a versão mais antiga, elas obterão a versão que funciona com o HTML.

Observe que, em uma das maiores implantações de versão em qualquer lugar da Internet, o jQuery usa números de versão no nome do arquivo real e permite com segurança coexistir várias versões sem nenhuma lógica especial do lado do servidor (cada versão é apenas um arquivo diferente).

Isso reduz o cache uma vez quando você implanta novas páginas e novos arquivos vinculados (que é o que você deseja) e, a partir daí, essas versões podem ser efetivamente armazenadas em cache (o que você também deseja).

jfriend00
fonte
Eu concordo com isso, mas é muito mais fácil ter o Sinatra anexado? V = <% = VERSION%> a todas as solicitações de css e js, em vez de ter que controlar cada arquivo individualmente. Eventualmente, mudaremos para o sinatra-assetpack, que pré-processará e compactará todos os arquivos e, na verdade, anexará uma versão # ao nome do arquivo, o que nos permitirá controlá-los individualmente com muito mais facilidade.
Brad Herman
1
Concordo que colocar o número da versão no nome do arquivo é a solução mais segura, se você quiser ter 10000% de certeza, mas não sigo o argumento "várias versões para existir de uma só vez". Um URL com um parâmetro de consulta é distinto do mesmo URL com um parâmetro de consulta diferente. Eles devem ser tratados como dois recursos diferentes pelo cliente; se não estiverem, o cliente está com problemas.
Pekka
2
@Pekka - o número da versão pode permitir a existência de várias versões ao mesmo tempo, mas isso requer cooperação do servidor para mapear o parâmetro de consulta para o arquivo real correto. Eu não acho que é isso que o OP está fazendo aqui e há poucas razões para exigir que essa complicação ao modificar o nome do arquivo seja muito mais simples e não precise de cooperação do servidor. Obviamente, ambos podem funcionar.
jfriend00
11

Como já foi dito, o bloqueio de cache com um parâmetro de consulta geralmente é considerado uma má ideia (tm) e existe há muito tempo. É melhor refletir a versão no nome do arquivo. O Boilerplate Html5 recomenda não usar a string de consulta, entre outros.

Dito isto, das recomendações que vi que citaram uma fonte, todas parecem ter sua sabedoria em um artigo de 2008 de Steve Souders. Suas conclusões são baseadas no comportamento dos proxies na época e podem ou não ser relevantes atualmente. Ainda assim, na ausência de informações mais atuais, alterar o nome do arquivo é a opção segura.

hashchange
fonte
9

Ele travará o cache uma vez, depois que o cliente tiver baixado o recurso, todas as outras respostas serão veiculadas no cache do cliente, a menos que:

  1. o parâmetro v é atualizado.
  2. o cliente limpa seu cache
ncremins
fonte
6

Em geral, isso deve ser bom, mas é possível que isso não funcione se houver um cache intermediário (um proxy) configurado para ignorar os parâmetros de solicitação.

Por exemplo, se você estiver fornecendo conteúdo estático através do Akamai CDN, ele poderá ser configurado para ignorar os parâmetros de solicitação para impedir o bloqueio do cache usando esse método.

Ken Liu
fonte
5

Depende muito de quão robusto você deseja que seu cache seja. Por exemplo, o servidor proxy squid (e possivelmente outros) assume como padrão não armazenar em cache URLs veiculadas com uma string de consulta - pelo menos quando o artigo foi escrito. Se você não se importa com certos casos de uso que causam falhas desnecessárias no cache, vá em frente com parâmetros de consulta. Mas é muito fácil configurar um esquema de bloqueio de cache baseado em nome de arquivo que evita esse problema.

Bobby Jack
fonte
5
O proxy squid citado no artigo de Steve Souders mudou sua política de cache padrão. Desde a versão 2.7 (maio de 2008) e versão 3.1 (março de 2010), o comportamento padrão é armazenar em cache o conteúdo dinâmico.
Josh cremalheira
5

Encontrei uma comparação das 2 técnicas (string de consulta vs nome do arquivo) aqui :

A versão como uma querystring tem dois problemas.

Primeiro, nem sempre é um navegador que implementa o cache pelo qual precisamos invadir. Diz-se que certos proxies (possivelmente mais antigos) ignoram a sequência de consultas com relação ao seu comportamento em cache.

Segundo, em certos cenários de implantação mais complexos, nos quais você tem vários servidores front-end e / ou vários servidores back-end, uma atualização é tudo instantânea. Você precisa poder fornecer a versão antiga e a nova de seus ativos ao mesmo tempo. Veja, por exemplo, como isso afeta você ao usar o Google App Engine.

do utilizador
fonte
4

Outra abordagem semelhante é usar o htaccess mod_rewrite para ignorar parte do caminho ao servir os arquivos. Sua página de índice nunca armazenada em cache faz referência ao caminho mais recente para os arquivos.

Do ponto de vista do desenvolvimento, é tão fácil quanto usar parâmetros para o número da versão, mas é tão robusto quanto a abordagem do nome do arquivo.

Use a parte ignorada do caminho para o número da versão e o servidor apenas o ignora e serve o arquivo não armazenado em cache.

1.2.3/css/styles.cssserve o mesmo arquivo css/styles.cssdesde que o primeiro diretório é removido e ignorado pelo arquivo htaccess

Incluindo arquivos com versão

<?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>

Observe que essa abordagem significa que você precisa desativar o cache da sua página de índice - Usando as tags <meta> para desativar o cache em todos os navegadores?

arquivo .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] 

Você pode adotar a mesma abordagem em qualquer plataforma de servidor que permita a reescrita de URL

(condição de reescrita adaptada do mod_rewrite - reescreva o diretório para a string de consulta, exceto / #! / )

... e se você precisar interromper o cache do ponto de entrada da página de índice / site, sempre poderá usar o JavaSript para atualizá-lo.

alexanderbird
fonte
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
fonte
Durante o desenvolvimento / teste de novos lançamentos, o cache pode ser um problema, porque o navegador, o servidor e, às vezes, a empresa de telecomunicações 3G (se você fizer uma implantação móvel) armazenará em cache o conteúdo estático (por exemplo, JS, CSS, HTML, img). Você pode superar isso anexando o número da versão, o número aleatório ou o carimbo de data / hora no URL, por exemplo: JSP: <script src = "js / excel.js? Time = <% = new java.util.Date ()%>"> </ script> Caso você esteja executando HTML puro (em vez de páginas JSP, ASP, PHP), o servidor não o ajudará. No navegador, os links são carregados antes da execução do JS, portanto, você deve remover os links e carregá-los com o JS
Conete Cristian
Eu não acho que isso carregará os arquivos JS em ordem, de forma síncrona.
Discrição rabino
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
fonte
0

Espero que isso ajude você a injetar um arquivo 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>

Fonte - Código Cachebuster em JavaScript

Vinit Kadkol
fonte