Cómo evitar el almacenamiento en caché de la página del navegador en Rails

151

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

La parte principal de mi sitio reacciona a tus clics. Por lo tanto, si hace clic en un enlace, lo enviará al destino y regenerará instantáneamente su página.

Pero, si presiona el botón Atrás, no verá la nueva página. Desafortunadamente, no se muestra sin una actualización manual; parece que el navegador lo está almacenando en caché. Quiero asegurarme de que el navegador no guarde en caché la página.

Por otra parte, yo no desea establecer lejano futuras fechas de caducidad de todos mis activos estáticos.

¿Cuál es la mejor manera de resolver esto? ¿Debo resolver esto en Rails? ¿Apache? Javascript?

Gracias por toda tu ayuda, Jason


Pobre de mí. Ninguna de estas sugerencias forzó el comportamiento que estoy buscando.

Tal vez hay una respuesta de JavaScript? Podría hacer que los rieles escriban una marca de tiempo en un comentario, luego haga que javascript verifique si los tiempos están dentro de los cinco segundos (o lo que sea que funcione). En caso afirmativo, bien, pero si no, ¿volver a cargar la página?

¿Crees que esto funcionaría?

Gracias por toda tu ayuda,

Jason

Jason Butler
fuente

Respuestas:

335

Finalmente descubrí esto: http://blog.serendeputy.com/posts/how-to-prevent-browsers-from-caching-a-page-in-rails/ enapplication_controller.rb

Después de 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 y versiones 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
fuente
3
¿Debería estar envuelto en un "if request.xhr?" por lo que solo se configura en actualizaciones de ajax, pero las páginas normales no.
MintDeparture
3
Realmente solo necesita Cache-Control: no-storemientras el navegador sea compatible con HTTP 1.1. Sección 14.9.2 Qué pueden almacenar los cachés
gaqzi
28
¡El 1 de enero de 1990 fue un lunes!
Jan Hettich
2
No funciona para mí. He agregado el mismo código en application_controller.rb y después de cerrar sesión, puedo ver la última página con el botón Atrás. Por favor guíame donde estoy equivocado?
Thorin
1
¿Esto NO también almacenará en caché JS y CSS en la aplicación rails? ¿Se cargarán JS y CSS desde el servidor para cada solicitud?
furiabhavesh
14

utilizar:

expires_now()

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

Sacre
fuente
3
expires_nowsolo envía el no-cacheencabezado. Dependiendo del navegador, esto podría no ser suficiente. (Por ejemplo, Firefox quiere una no-storeconexión no HTTPS: developer.mozilla.org/en/docs/Using_Firefox_1.5_caching )
Daniel Rikowski
1
De acuerdo, esta no es una solución válida, no-storetambién se necesita probar con Rails 5.2 y Chrome 77 .
AndrewSouthpaw
3

He usado esta línea con cierto éxito en el controlador. Funciona en Safari e Internet Explorer, pero no lo he visto funcionar con Firefox.

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

Para su segundo punto, si usa los métodos de ayuda de rails como

stylesheet_link_tag

y deje la configuración predeterminada en su servidor web, los activos generalmente se almacenan en caché bastante bien.

erik
fuente
3
1.year.agoes innecesaria sobrecarga. Simplemente elija un momento arbitrario en el pasado comoFri, 01 Jan 1990 00:00:00 GMT
Archonic
66
@Archonic 1 de enero de 1990 fue un lunes!
Thomas R. Koll
1

La forma más limpia sería escribir un middleware Rack, que cambia el encabezado Cache-Control en función de alguna lógica (por ejemplo, solo para la aplicación / xml tipo mime). O, para un enfoque más feo pero que todavía funciona, uno podría cambiar la constante ActionDispatch :: Response :: DEFAULT_CACHE_CONTROL a 'no-cache'. Por supuesto, si se requiere el controlador y / o la granularidad de la acción, entonces es mejor hacerlo en el controlador.

romano
fuente
1

Punto de nota: no puede borrar condicionalmente el caché (como si un before_filtersolo llama reset_cachesi el usuario ya ha estado allí). Necesita borrar incondicionalmente el caché, porque el navegador no realizará una nueva solicitud solo para ver si esta vez, necesita recargarse, aunque no fue necesario que la última vez.

Ejemplo:

before_filter :reset_cache, if: :user_completed_demographics?

no funcionará para evitar que los usuarios regresen después de haber estado allí, ya que el navegador usa los encabezados de caché originales en el botón Atrás.

before_filter :reset_cache

sin embargo, funcionará (después de actualizar la página y borrar el caché antes de agregar esto, obviamente), ya que, en la primera solicitud, el navegador obtendrá el no-cache, no-store, ...y lo aplicará a futuras cargas de página.

BalinKingOfMoria Reinstate CMs
fuente
0

no_cache_control Joya.

Si necesita hacer esto para todas las respuestas, por ejemplo, para pasar una prueba de penetración (BURP, Detectify, etc.), puede instalar esta Gema en Rails 4+ para agregar los siguientes encabezados a todas las respuestas:

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

Funciona a las mil maravillas y es realmente el camino correcto para las aplicaciones web HTTPS seguras que requieren autenticación para hacer cualquier cosa.

Joshua Pinter
fuente