paperclip/server/src
Jannes Stubbemann 0d87fd9a11
fix: proper cache headers for static assets and SPA fallback (#3734)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Every deployment serves the same Vite-built UI bundle from the same
express app
> - Vite emits JS/CSS under `/assets/<name>.<hash>.<ext>` — the hash
rolls whenever the content rolls, so these files are inherently
immutable
> - `index.html` references specific hashed filenames, so it has the
opposite lifecycle: whenever we deploy, the file changes but the URL
doesn't
> - Today the static middleware sends neither with cache headers, and
the SPA fallback serves `index.html` for any unmatched route — including
paths under `/assets/` that no longer exist after a deploy
> - That combination produces the familiar "blank screen after deploy" +
`Failed to load module script: Expected a JavaScript MIME type but
received 'text/html'` bug
> - This pull request caches hashed assets immutably, forces
`index.html` to `no-cache` everywhere it gets served, and returns 404
for missing `/assets/*` paths

## What Changed

- `server/src/app.ts`:
- Serve `/assets/*` with `Cache-Control: public, max-age=31536000,
immutable`.
- Serve the remaining static files (favicon, manifest, robots.txt) with
a 1-hour cache, but override to `no-cache` specifically for `index.html`
via the `setHeaders` hook — because `express.static` serves it directly
for `/` and `/index.html`.
- The SPA fallback (`app.get(/.*/, …)`) sets `Cache-Control: no-cache`
on its `index.html` response.
- The fallback returns 404 for paths under `/assets/` so browsers don't
cache the HTML shell as a JavaScript module.

## Verification

- `curl -i http://localhost:3100/assets/index-abc123.js` →
`cache-control: public, max-age=31536000, immutable`.
- `curl -i http://localhost:3100/` → `cache-control: no-cache`.
- `curl -i http://localhost:3100/assets/missing.js` → `404`.
- `curl -i http://localhost:3100/some/spa/route` → `200` HTML with
`cache-control: no-cache`.

## Risks

Low. Asset URLs and HTML content are unchanged; only response headers
and the 404 behavior for missing asset paths change. No API surface
affected.

## Model Used

Claude Opus 4.6 (1M context), extended thinking mode.

## Checklist

- [x] Thinking path traces from project context to this change
- [x] Model used specified
- [x] Tests run locally and pass
- [x] CI green
- [x] Greptile review addressed
2026-04-15 09:45:22 -05:00
..
__tests__ [codex] harden authenticated routes and issue editor reliability (#3741) 2026-04-15 08:41:15 -05:00
adapters feat(adapters): add capability flags to ServerAdapterModule (#3540) 2026-04-15 07:10:52 -05:00
auth fix: remove hardcoded JWT secret fallback from createBetterAuthInstance 2026-04-08 17:51:21 +03:00
middleware fix: trust PAPERCLIP_PUBLIC_URL in board mutation guard (#3731) 2026-04-15 09:42:55 -05:00
onboarding-assets [codex] Harden execution reliability and heartbeat tooling (#3679) 2026-04-14 13:34:52 -05:00
realtime update typing to node v24 from v20 2026-03-05 14:36:00 -03:00
routes [codex] harden authenticated routes and issue editor reliability (#3741) 2026-04-15 08:41:15 -05:00
secrets refactor: rename packages to @paperclipai and CLI binary to paperclipai 2026-03-03 08:45:26 -06:00
services [codex] harden authenticated routes and issue editor reliability (#3741) 2026-04-15 08:41:15 -05:00
storage refactor: rename packages to @paperclipai and CLI binary to paperclipai 2026-03-03 08:45:26 -06:00
types Add browser-based board CLI auth flow 2026-03-23 08:46:05 -05:00
agent-auth-jwt.ts fix(agent-auth): fall back to BETTER_AUTH_SECRET when PAPERCLIP_AGENT_JWT_SECRET is absent 2026-04-05 19:10:00 +00:00
app.ts fix: proper cache headers for static assets and SPA fallback (#3734) 2026-04-15 09:45:22 -05:00
attachment-types.ts Allow arbitrary issue attachments 2026-04-06 21:24:12 -05:00
board-claim.ts refactor: rename packages to @paperclipai and CLI binary to paperclipai 2026-03-03 08:45:26 -06:00
config-file.ts refactor: rename packages to @paperclipai and CLI binary to paperclipai 2026-03-03 08:45:26 -06:00
config.ts Harden tailnet bind setup 2026-04-11 07:13:41 -05:00
dev-runner-worktree.ts fix(dev-runner): tighten worktree env bootstrap 2026-04-11 08:35:53 -05:00
dev-server-status.ts Guard dev health JSON parsing 2026-04-06 21:23:33 -05:00
dev-watch-ignore.ts feat(adapters): external adapter plugin system with dynamic UI parser 2026-04-03 21:11:20 +01:00
errors.ts Add server routes for companies, approvals, costs, and dashboard 2026-02-17 09:07:27 -06:00
home-paths.ts Redesign project codebase configuration 2026-03-16 15:56:37 -05:00
index.ts fix(server): respect externally set PAPERCLIP_API_URL env var (#3472) 2026-04-15 06:43:48 -05:00
log-redaction.ts Add username log censor setting 2026-03-20 08:50:00 -05:00
paths.ts feat(cli): add client commands and home-based local runtime defaults 2026-02-20 07:10:58 -06:00
redaction.ts Implement secrets service with local encryption, redaction, and runtime resolution 2026-02-19 15:43:52 -06:00
startup-banner.ts Introduce bind presets for deployment setup 2026-04-11 07:09:07 -05:00
telemetry.ts fix: add periodic flush and graceful shutdown for server-side telemetry 2026-04-02 10:47:29 -05:00
ui-branding.ts Add worktree UI branding 2026-03-13 11:12:43 -05:00
version.ts add app version label 2026-03-17 09:40:07 +05:30
vite-html-renderer.ts [codex] Harden execution reliability and heartbeat tooling (#3679) 2026-04-14 13:34:52 -05:00
worktree-config.ts Avoid sibling worktree port collisions 2026-03-26 11:12:39 -05:00