Skip to content

Commit be70adc

Browse files
kckernclaude
andcommitted
perf(web): let browsers cache public assets for an hour
Live sends no Cache-Control on its assets, so browsers kept them by heuristic for months. Node sent max-age=0, which made every page view revalidate every stylesheet, script and image. An hour removes those round trips within a visit; asset names are not fingerprinted, so a deploy still reaches browsers within the hour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent d284546 commit be70adc

2 files changed

Lines changed: 10 additions & 1 deletion

File tree

‎apps/web/src/app/middleware/static.js‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ function setHeaders(res, filePath) {
1111
if (path.extname(filePath).toLowerCase() === '.ico') res.setHeader('Content-Type', 'image/vnd.microsoft.icon');
1212
}
1313

14+
// An hour of browser caching: live sent no Cache-Control and browsers kept its assets by
15+
// heuristic, while max-age=0 revalidated every asset on every page. Names are not fingerprinted,
16+
// so a deploy reaches browsers within the hour.
1417
export function publicStatic(dir = PUBLIC_DIR) {
15-
return express.static(dir, { setHeaders });
18+
return express.static(dir, { setHeaders, maxAge: '1h' });
1619
}
1720

1821
// FONT_DIR (licensed fonts that must not be published) at /private-fonts; no route when unset.

‎apps/web/test/unit/staticServe.test.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ describe('public static files', () => {
1919
expect(res.headers['content-type']).toBe('image/vnd.microsoft.icon');
2020
expect(Buffer.compare(res.body, readFileSync(new URL('../../public/favicon.ico', import.meta.url)))).toBe(0);
2121
});
22+
it('lets browsers keep assets for an hour instead of revalidating on every page', async () => {
23+
// Live sends no Cache-Control, so browsers kept its year-old assets by heuristic; max-age=0
24+
// made every page view revalidate each asset
25+
const res = await request(app).get('/css/style.css');
26+
expect(res.headers['cache-control']).toBe('public, max-age=3600');
27+
});
2228
it('leaves other types alone', async () => {
2329
const res = await request(app).get('/js/main.js');
2430
expect(res.status).toBe(200);

0 commit comments

Comments
 (0)