index.html wurde ein Jahr lang zwischengespeichert
Die maxAge-Option von fastify-static setzt den Cache-Header fuer jede Datei, auch fuer die index.html. Der setHeaders-Callback, der das fuer HTML wieder aufheben sollte, kam dagegen nicht an: ausgeliefert wurde durchgehend public, max-age=31536000. Damit bekam ein Browser nach einem Update weiter die alte Oberflaeche - ein Jahr lang, ohne Fehlermeldung, obwohl der Server laengst neue Dateien hatte. Genau so ist der Abgleich-Knopf nach dem Deploy unsichtbar geblieben. Jetzt setzt die App beide Faelle selbst: HTML immer neu holen, Assets unbegrenzt behalten. Deren Dateinamen enthalten einen Inhalts-Hash, sie aendern sich bei neuem Inhalt ohnehin. Drei Tests pruefen die Header, weil ein Fehler an dieser Stelle nirgends auffaellt - er sieht aus, als waere das Update nicht angekommen.
This commit is contained in:
+11
-4
@@ -128,11 +128,18 @@ export async function buildApp(): Promise<FastifyInstance> {
|
|||||||
await app.register(fastifyStatic, {
|
await app.register(fastifyStatic, {
|
||||||
root: webRoot,
|
root: webRoot,
|
||||||
prefix: '/',
|
prefix: '/',
|
||||||
// Gehashte Assets duerfen lange liegen bleiben, index.html nie.
|
// maxAge wuerde den Header fuer *alle* Dateien setzen und dabei auch die
|
||||||
maxAge: '1y',
|
// index.html erwischen - die muss aber bei jedem Aufruf neu geholt werden,
|
||||||
|
// sonst zeigt der Browser nach einem Update monatelang die alte Oberflaeche.
|
||||||
|
// Deshalb hier abschalten und beides unten selbst setzen.
|
||||||
|
cacheControl: false,
|
||||||
setHeaders: (res, path) => {
|
setHeaders: (res, path) => {
|
||||||
if (path.endsWith('index.html')) {
|
if (path.endsWith('.html')) {
|
||||||
res.setHeader('Cache-Control', 'no-cache');
|
res.setHeader('Cache-Control', 'no-cache, must-revalidate');
|
||||||
|
} else {
|
||||||
|
// Assets tragen einen Inhalts-Hash im Namen: aendert sich der Inhalt,
|
||||||
|
// aendert sich der Dateiname. Die duerfen beliebig lange liegen.
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -286,3 +286,36 @@ describe('Guthaben ueber die API', () => {
|
|||||||
assert.equal(response.json().error, 'NOT_FOUND');
|
assert.equal(response.json().error, 'NOT_FOUND');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Auslieferung der Oberflaeche', () => {
|
||||||
|
// Diese Header sind der Grund, warum ein Update beim Nutzer ankommt. Wird die
|
||||||
|
// index.html cachebar ausgeliefert, sieht er nach einem Deploy weiter die
|
||||||
|
// alte Oberflaeche - ohne dass irgendwo ein Fehler auftaucht.
|
||||||
|
it('laesst die index.html nicht zwischenspeichern', async () => {
|
||||||
|
const response = await app.inject({ method: 'GET', url: '/' });
|
||||||
|
|
||||||
|
assert.equal(response.statusCode, 200);
|
||||||
|
assert.match(
|
||||||
|
response.headers['cache-control'] as string,
|
||||||
|
/no-cache/,
|
||||||
|
'die index.html muss bei jedem Aufruf neu geholt werden',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('liefert auch Deep-Links ohne Cache aus', async () => {
|
||||||
|
const response = await app.inject({ method: 'GET', url: '/players/1' });
|
||||||
|
|
||||||
|
assert.equal(response.statusCode, 200);
|
||||||
|
assert.match(response.headers['cache-control'] as string, /no-cache/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('laesst gehashte Assets lange liegen', async () => {
|
||||||
|
const page = await app.inject({ method: 'GET', url: '/' });
|
||||||
|
const asset = /\/assets\/[^"]+\.js/.exec(page.body)?.[0];
|
||||||
|
assert.ok(asset, 'die Seite verweist auf ein Asset');
|
||||||
|
|
||||||
|
const response = await app.inject({ method: 'GET', url: asset });
|
||||||
|
assert.equal(response.statusCode, 200);
|
||||||
|
assert.match(response.headers['cache-control'] as string, /max-age=31536000/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user