So verhindern Sie das Zwischenspeichern von Browserseiten in Rails

150

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

Der Hauptteil meiner Website reagiert auf Ihre Klicks. Wenn Sie also auf einen Link klicken, werden Sie zum Ziel weitergeleitet und Ihre Seite wird sofort neu generiert.

Wenn Sie jedoch auf die Schaltfläche "Zurück" klicken, wird die neue Seite nicht angezeigt. Leider wird es nicht ohne manuelle Aktualisierung angezeigt. Es scheint, dass der Browser es zwischenspeichert. Ich möchte sicherstellen, dass der Browser die Seite nicht zwischenspeichert.

Getrennt davon wurde ich tun möchte weit Zukunft Ablaufdaten für alle meine statische Vermögenswerte setzen.

Was ist der beste Weg, um dies zu lösen? Soll ich das in Rails lösen? Apache? Javascript?

Danke für all deine Hilfe, Jason


Ach. Keiner dieser Vorschläge hat das gesuchte Verhalten erzwungen.

Vielleicht gibt es eine Javascript-Antwort? Ich könnte Rails einen Zeitstempel in einen Kommentar schreiben lassen und dann das Javascript überprüfen lassen, um festzustellen, ob die Zeiten innerhalb von fünf Sekunden liegen (oder was auch immer funktioniert). Wenn ja, dann gut, aber wenn nein, dann die Seite neu laden?

Glaubst du, das würde funktionieren?

Danke für deine Hilfe,

Jason

Jason Butler
quelle

Antworten:

334

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

After 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 und ältere Versionen:

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
quelle
3
Sollte dies in eine "if request.xhr" eingeschlossen werden? Also wird es nur bei Ajax-Aktualisierungen eingestellt, die normalen Seiten jedoch nicht?
MintDeparture
3
Sie brauchen nur wirklich, Cache-Control: no-storesolange der Browser mit HTTP 1.1 kompatibel ist. Abschnitt 14.9.2 Was von Caches gespeichert werden kann
gaqzi
28
Der 1. Januar 1990 war ein Montag!
Jan Hettich
2
Es funktioniert nicht für mich. Ich habe den gleichen Code in application_controller.rb hinzugefügt und nach dem Abmelden kann ich die letzte Seite mit der Schaltfläche "Zurück" sehen. Bitte führen Sie mich, wo ich falsch liege?
Thorin
1
Wird dies auch NICHT JS und CSS in der Rails-App zwischenspeichern? Werden JS und CSS für jede Anforderung vom Server geladen?
Furiabhavesh
14

verwenden:

expires_now()

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

Sacre
quelle
3
expires_nowsendet nur den no-cacheHeader. Je nach Browser reicht dies möglicherweise nicht aus. (Zum Beispiel will Firefox eine no-storefür Nicht-HTTPS-Verbindungen: developer.mozilla.org/en/docs/Using_Firefox_1.5_caching )
Daniel Rikowski
1
Einverstanden, dies ist keine gültige Lösung, getestet mit Rails 5.2 und Chrome 77. no-storewird ebenfalls benötigt.
AndrewSouthpaw
3

Ich habe diese Linie mit einigem Erfolg in der Steuerung verwendet. Es funktioniert in Safari und Internet Explorer, aber ich habe nicht gesehen, dass es mit Firefox funktioniert.

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

Für Ihren zweiten Punkt, wenn Sie die Rails-Hilfsmethoden wie verwenden

stylesheet_link_tag

Wenn Sie die Standardeinstellungen auf Ihrem Webserver belassen, werden die Assets normalerweise recht gut zwischengespeichert.

erik
quelle
3
1.year.agoist unnötiger Aufwand. Fri, 01 Jan 1990 00:00:00 GMT
Wählen Sie
6
@Archonic 1. Januar 1990 war ein Montag!
Thomas R. Koll
1

Der sauberere Weg wäre, eine Rack-Middleware zu schreiben, die den Cache-Control-Header basierend auf einer Logik ändert (zum Beispiel nur für den MIME-Typ application / xml). Für einen hässlicheren, aber immer noch funktionierenden Ansatz könnte die Konstante ActionDispatch :: Response :: DEFAULT_CACHE_CONTROL in 'no-cache' geändert werden. Wenn die Controller- und / oder Aktionsgranularität erforderlich ist, ist es natürlich besser, dies im Controller zu tun.

römisch
quelle
1

Hinweis: Sie können den Cache nicht bedingt leeren (z. B. wenn a before_filternur aufruft, reset_cachewenn der Benutzer bereits dort war). Sie müssen den Cache bedingungslos leeren, da der Browser keine neue Anforderung stellt, nur um festzustellen, ob er diesmal neu geladen werden muss, obwohl dies nicht das letzte Mal erforderlich war.

Beispiel:

before_filter :reset_cache, if: :user_completed_demographics?

funktioniert nicht, um zu verhindern, dass Benutzer zurückkehren, nachdem sie dort waren, da der Browser die ursprünglichen Cache-Header auf der Schaltfläche Zurück verwendet.

before_filter :reset_cache

funktioniert jedoch (nachdem Sie die Seite aktualisiert und den Cache gelöscht haben, bevor Sie dies hinzugefügt haben, offensichtlich), da der Browser bei der ersten Anforderung das abruft no-cache, no-store, ...und es auf zukünftige Seitenladevorgänge anwendet.

BalinKingOfMoria CMs wiederherstellen
quelle
0

no_cache_control Juwel.

Wenn Sie dies für alle Antworten tun müssen, z. B. um einen Penetrationstest (BURP, Detectify usw.) zu bestehen, können Sie diesen Gem on Rails 4+ installieren, um allen Antworten die folgenden Header hinzuzufügen:

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

Funktioniert wie ein Zauber und ist wirklich der richtige Weg für sichere HTTPS-Webanwendungen, für die eine Authentifizierung erforderlich ist.

Joshua Pinter
quelle