Como impedir o cache da página do navegador no Rails

151

Ubuntu -> Apache -> Phusion Passenger -> Rails 2.3

A parte principal do meu site reage aos seus cliques. Portanto, se você clicar em um link, ele o enviará ao destino e regenerará instantaneamente sua página.

Mas, se você pressionar o botão Voltar, não verá a nova página. Infelizmente, ele não aparece sem uma atualização manual; parece que o navegador está em cache. Quero garantir que o navegador não armazene em cache a página.

Separadamente, eu faço deseja definir far-futuros datas de validade para todos os meus bens estáticos.

Qual é a melhor maneira de resolver isso? Devo resolver isso no Rails? Apache? Javascript?

Obrigado por toda a sua ajuda, Jason


Alas. Nenhuma dessas sugestões forçou o comportamento que estou procurando.

Talvez haja uma resposta javascript? Eu poderia fazer com que os trilhos escrevessem um carimbo de data e hora em um comentário e depois fiz o javascript verificar se os tempos estão dentro de cinco segundos (ou o que funcionar). Se sim, tudo bem, mas se não, recarregue a página?

Você acha que isso funcionaria?

Obrigado por toda sua ajuda,

Jason

Jason Butler
fonte

Respostas:

335

Finalmente resolvi isso - http://blog.serendeputy.com/posts/how-to-prevent-browsers-from-caching-a-page-in-rails/ inapplication_controller.rb

Após o Rails 5:

class ApplicationController < ActionController::Base

  before_action :set_cache_headers

  private

  def set_cache_headers
    response.headers["Cache-Control"] = "no-cache, no-store"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "Mon, 01 Jan 1990 00:00:00 GMT"
  end
end

Rails 4 e versões anteriores:

class ApplicationController < ActionController::Base

  before_filter :set_cache_headers

  private

  def set_cache_headers
    response.headers["Cache-Control"] = "no-cache, no-store"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "Mon, 01 Jan 1990 00:00:00 GMT"
  end
end
Jason Butler
fonte
3
Isso deve ser envolto em um "if request.xhr?" então ele só é definido em atualizações de ajax, mas as páginas normais não?
MintDeparture
3
Você realmente precisa apenas Cache-Control: no-storedesde que o navegador seja compatível com HTTP 1.1. Seção 14.9.2 O que pode ser armazenada por caches
gaqzi
28
1 de janeiro de 1990, foi uma segunda-feira!
Jan Hettich
2
Não está funcionando para mim. Adicionei o mesmo código em application_controller.rb e, após o logout, consigo ver a última página pelo botão Voltar. Por favor, me guie onde estou errado?
quer
1
Isso NÃO também armazenará em cache JS e CSS no aplicativo Rails? JS e CSS serão carregados do servidor para cada solicitação?
22615 furbanhavesh
14

usar:

expires_now()

http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-expires_now

Sacre
fonte
3
expires_nowenvia apenas o no-cachecabeçalho. Dependendo do navegador, isso pode não ser suficiente. (Por exemplo, o Firefox deseja um no-storepara conexões que não sejam HTTPS: developer.mozilla.org/en/docs/Using_Firefox_1.5_caching )
Daniel Rikowski
1
De acordo, essa não é uma solução válida, testada com o Rails 5.2 e o Chrome 77. no-storetambém é necessária.
AndrewSouthpaw
3

Eu usei esta linha com algum sucesso no controlador. Funciona no Safari e no Internet Explorer, mas não o vi funcionar com o Firefox.

response.headers["Expires"] = "#{1.year.ago}"

Para seu segundo ponto, se você usar os métodos auxiliares de trilhos, como

stylesheet_link_tag

e deixar as configurações padrão em seu servidor da web, os ativos normalmente são armazenados em cache muito bem.

erik
fonte
3
1.year.agosobrecarga desnecessária. Basta escolher um tempo arbitrário no passado, comoFri, 01 Jan 1990 00:00:00 GMT
Archonic
6
@Archonic 1 de janeiro de 1990 foi uma segunda-feira!
Thomas R. Koll
1

A maneira mais limpa seria escrever um middleware de rack, que altera o cabeçalho de controle de cache com base em alguma lógica (por exemplo, apenas para application / xml mime-type). Ou, para uma abordagem mais feia, mas ainda funcionando, pode-se alterar a constante ActionDispatch :: Response :: DEFAULT_CACHE_CONTROL para 'no-cache'. Obviamente, se a granularidade do controlador e / ou ação for necessária, é melhor fazer isso no controlador.

romano
fonte
1

Nota: você não pode limpar o cache condicionalmente (como se before_filterapenas ligasse reset_cachese o usuário já estivesse lá). Você precisa incondicionalmente limpar o cache , porque o navegador não fará uma nova solicitação apenas para verificar se, dessa vez, precisa ser recarregado, mesmo que não precise da última vez.

Exemplo:

before_filter :reset_cache, if: :user_completed_demographics?

não funcionará para impedir que os usuários voltem depois que eles estiverem lá, pois o navegador usa os cabeçalhos de cache originais no botão Voltar.

before_filter :reset_cache

funcionará, no entanto (depois de atualizar a página e limpar o cache antes de você adicionar isso, obviamente), pois, na primeira solicitação, o navegador obterá no-cache, no-store, ...e aplicará a futuras carregamentos de página.

Restabelecer CMs BalinKingOfMoria
fonte
0

no_cache_control Gema.

Se você precisar fazer isso para todas as respostas, por exemplo, para passar em um teste de penetração (BURP, Detectify, etc.), poderá instalar esta Gem on Rails 4+ para adicionar os seguintes cabeçalhos a todas as respostas:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: -1

Funciona como um encanto e é realmente o caminho certo para aplicativos da Web HTTPS seguros que exigem autenticação para fazer qualquer coisa.

Joshua Pinter
fonte