nginx-Cache, der keine eingeloggte Seite sehen darf
Ein 10-Sekunden-HTML-Microcache im Shared Memory ist billig. Er ist falsch, sobald auth_token am Request hängt. Bypass über das Cookie, nicht über eine Vermutung.
Vue SSR sitzt hinter nginx. Nach einem Deploy ist ein anonymer GET einer Notizenseite zehn Sekunden dasselbe HTML. Ich habe das in fastcgi_cache mit einer 8-MB-Key-Zone gelegt. Die Node-CPU sank. Dann bekam ein Fremder in diesen zehn Sekunden meine Admin-Hülle, weil der Cache-Key nur die URL war.
Sanctum hält die Session im httpOnly-Cookie auth_token. Das HTML eines eingeloggten Users ist kein privater Artikelbody. Es ist ein anderer Header, ein anderes Menü, ein anderer Leerzustand. Eine gecachte Seite mit diesem Markup reicht. nginx lieferte sie als HIT an den nächsten anonymen Besucher.
Nutzer der Public-Site sahen Editor-Chrome, den sie nie sehen sollten. Ich sah einen Cache, der einen Cookie-Request nicht von einem öffentlichen unterscheiden konnte.
Bypass über das Cookie
Die Regel in nginx ist eine Zeile: hat der Request auth_token, den Cache überspringen. Dasselbe für das CSRF-Cookie bei POST. /api cache ich gar nicht. JSON aus LaraBoom liegt schon in Redis, wenn es sich lohnt, und ein gecachtes 401 ist schlechter als ein langsames 401.
Die Zone lebt im nginx-Worker, nicht in Redis. Das ist der Punkt. Redis hält schon Listen-Payloads. nginx hält die gerenderte Hülle. Sie zu mischen heißt, ein Purge muss zwei Stores ansprechen, und einen davon vergesse ich.
Auf der Notes-Location habe ich $upstream_cache_status geloggt. Nach dem Login musste dieselbe URL MISS oder BYPASS zeigen, nie HIT. Ein HIT mit meinem Namen im Body hieß, der Key ignorierte das Cookie weiter. Vary on Cookie hätte die Zone in einen Eintrag pro Session geschnitten. Bypass ist billiger und speichert privates HTML gar nicht.
Was ich messe
fastcgi_cache_bypass und fastcgi_no_cache brauchen dieselbe Bedingung. Bypass beim Lesen, no_cache beim Schreiben. Steht nur eines, landet eine eingeloggte Antwort trotzdem in der Zone, und der nächste anonyme Besucher kann sie bekommen. Das war der Bug: Bypass an, no_cache aus, und meine Hülle schrieb einen öffentlichen Key.
Nach dem Login treffe ich dieselbe URL zweimal, einmal mit Cookie, einmal ohne. Der erste muss missen und zu Node. Der zweite, ohne Cookie, darf HIT sein. Steht in dem HIT-Body mein Name, ist der Bypass falsch.
TTL bleibt 10 Sekunden. Länger sitzt eine Admin-Publikation hinter nginx, obwohl Redis schon abgelaufen ist. Kürzer arbeitet die Zone nicht. Cache überspringe ich außerdem bei allem, das nicht GET ist. Ein HEAD vom Probe darf kein HTML füllen. Query-Strings der öffentlichen Notizenliste gehören nur in den Key, wenn sie die Seite ändern. Tracking-Params nicht.
Was ich daraus mitnehme
Ich messe den Cache-Status mit und ohne auth_token auf derselben URL. Ein HIT mit einem Anzeigenamen ist ein Leak, auch bei 10 Sekunden.
Einem Cache-Key nur aus dem Pfad traue ich nicht. Bypass und no_cache müssen die Cookie-Bedingung teilen. Vary on Cookie speichert private Seiten. Die Zone lieber überspringen.
Die Regel, die ich behalte: Cookie-Requests schreiben nie einen öffentlichen HTML-Key. JSON bleibt bei PHP und Redis. nginx cached nur anonyme GET-Hüllen.
