Der Deploy war grün, der Spieler sah trotzdem das alte Spiel
· auslieferung · werkzeuge
Ein Godot-Web-Export besteht aus vier Dateien, die zusammenpassen müssen: die
index.html, ein Loader, die .pck mit den Daten und die .wasm mit dem
Programm. Dazu ein Service Worker, der alles ablegt, damit das Spiel auch ohne
Netz startet.
Genau diese Zwischenschicht ist das Problem. Ein Service Worker ist dafür da, alte Dateien auszuliefern. Wenn er die falsche Vorstellung davon hat, welche alt sind, liefert er sie eben weiter — beliebig lange.
Der Befund
Nach dem Ausliefern lief im Browser weiter der vorherige Build. Nicht ein paar
Minuten: dauerhaft, über mehrere Neuladen hinweg. Am Server lag die neue
Fassung, mit no-store in der Antwort. Nachgemessen kam trotzdem die alte an.
Die Ursache
Cloudflare überschrieb Cache-Control für .js mit der Browser-TTL der
Zone — vier Stunden. Damit blieb der alte index.service.worker.js im
Browser gültig, der alte Cache blieb gültig, und der Spieler lief weiter im
alten Build.
Das ist die unangenehme Sorte Fehler: Jede einzelne Stelle sieht korrekt aus. Der Server setzt die richtige Kopfzeile. Der Worker macht, was ein Worker macht. Der Browser hält sich an das, was ihm gesagt wurde. Nur wurde ihm etwas anderes gesagt, als wir geschrieben hatten.
Zwei Wege, und wir sind beide gegangen
Der eine braucht Cloudflare-Zugriff: browser_cache_ttl = 0 setzen —
„Respect Existing Headers“. Das ist die richtige Einstellung, und sie steht
jetzt so. Zurückdrehen ginge mit
PATCH /zones/<id>/settings/browser_cache_ttl {"value": 14400}
Der andere braucht ihn nicht — und das ist der wichtigere. deploy.sh
schreibt beim Export den Build-Fingerabdruck in die Dateinamen von Loader
und Service Worker:
index-a58ca516be.js
index-sw-a58ca516be.js
und zieht die Verweise in index.html und im Worker mit. Die index.html
selbst wird als no-store ausgeliefert — das respektiert Cloudflare —, holt
also immer die neuen Namen. Ein alter Cache-Eintrag kann damit keine alte
Fassung mehr am Leben halten: er zeigt auf einen Dateinamen, den niemand mehr
anfragt.
Verworfen: auf die richtige Einstellung vertrauen. Sie steht jetzt richtig — aber sie steht in einem Konto, das jemand anders auch bedienen kann, und sie gilt je Zone. Der Fingerabdruck im Dateinamen ist Gürtel und Hosenträger, und er kostet zehn Zeilen im Auslieferungsskript. Eine Einstellung, deren Verletzung man erst Wochen später bemerkt, ist keine Zusicherung.
Live nachgemessen, nicht angenommen
Der Beleg: ein Browser, der noch auf dem alten Worker
(index.service.worker.js) hing, lief nach einem normalen Neuladen auf
index-sw-a58ca516be.js und dem neuen Loader. Kein Leeren des Caches, kein
Entwicklermodus.
Das ist der Unterschied zwischen „behoben“ und „müsste behoben sein“. Am Quelltext gemessen sieht beides gleich aus.
Ein Nachtrag, der fast vier Wochen dauerte
Diese Geschichte hatte einen zweiten Teil. Dieselbe Zoneneinstellung stand für
pipfold.com noch immer auf 14400, als die Domain im August dazukam — und sie
hat dort dasselbe angerichtet, nur an einer anderen Datei. Aufgefallen ist es
erst, als eine auf dem iPhone abgelegte Fassung bei jedem Öffnen 39 MB neu lud.
Eine Falle, die man einmal verstanden hat, schnappt beim nächsten Hostnamen
trotzdem wieder zu. Der voriger Wert liegt inzwischen in
~/.config/mana/cf-browsercachettl-pipfold-vor-20260819.json — damit die
nächste Zone wenigstens nachschlagen kann, wonach sie suchen muss.
Nebenbefund aus demselben Tag, weil er lange nach einem Fehler im Spiel
aussah: Standbilder mitten im Würfelwurf. Der Wurf blieb hängen, die Würfel
standen in der Luft. Das war kein Fehler in der Bewegung, sondern die
Drosselung eines Hintergrund-Tabs in Chrome — document.hidden,
requestAnimationFrame feuert nicht. Vor solchen Diagnosen erst
document.visibilityState prüfen.