What happens
GET /sw.js on the dev server returns Vite's SPA fallback (index.html) with Content-Type: text/html.
Browsers refuse a service-worker script that isn't a JavaScript MIME type. The practical consequence is that the service-worker update check silently fails, so a worker registered on this origin by some other app can never be replaced or evicted — it just keeps serving.
Why it bites
5173 is Vite's default port, so any other app previously dev-served on this machine shares the origin — and a service worker registration outlives the process that installed it.
Concretely: another project (a SvelteKit PWA also defaulting to :5173) had registered a worker whose fetch handler answers every navigate request from its cached "/". Long after that dev server was stopped, it was still intercepting localhost:5173/admin and serving its own shell. The Instatic admin was completely unreachable in the browser while curl worked perfectly — which is the signature of this class of bug and takes a while to recognise.
Because /sw.js 404s into the HTML fallback, the browser could never fetch a replacement worker, so the situation was unrecoverable without manually unregistering via DevTools.
Suggested fix
Either would help; the first is the smaller change:
- Serve a
public/sw.js kill-switch — a valid JS worker that clears caches, calls self.registration.unregister(), and registers no fetch handler. Any stale worker on the origin then evicts itself on the next update check. Verified working: registering it removed the registration and cleared all caches, and /admin loaded normally afterwards.
- Don't fall back to
index.html for requests that clearly aren't navigations (.js, .json, .map, …) — return a real 404 with a non-HTML content type.
Happy to open a PR for (1) if you'd take it.
Environment
- Instatic at
949ddb2d (v0.0.17 dev), bun run dev, Vite on :5173
- Chrome / Chromium
🤖 Generated with Claude Code
https://claude.ai/code/session_01FPTjUK1YJcwK83zYoANRbb
What happens
GET /sw.json the dev server returns Vite's SPA fallback (index.html) withContent-Type: text/html.Browsers refuse a service-worker script that isn't a JavaScript MIME type. The practical consequence is that the service-worker update check silently fails, so a worker registered on this origin by some other app can never be replaced or evicted — it just keeps serving.
Why it bites
5173is Vite's default port, so any other app previously dev-served on this machine shares the origin — and a service worker registration outlives the process that installed it.Concretely: another project (a SvelteKit PWA also defaulting to
:5173) had registered a worker whosefetchhandler answers every navigate request from its cached"/". Long after that dev server was stopped, it was still interceptinglocalhost:5173/adminand serving its own shell. The Instatic admin was completely unreachable in the browser whilecurlworked perfectly — which is the signature of this class of bug and takes a while to recognise.Because
/sw.js404s into the HTML fallback, the browser could never fetch a replacement worker, so the situation was unrecoverable without manually unregistering via DevTools.Suggested fix
Either would help; the first is the smaller change:
public/sw.jskill-switch — a valid JS worker that clears caches, callsself.registration.unregister(), and registers nofetchhandler. Any stale worker on the origin then evicts itself on the next update check. Verified working: registering it removed the registration and cleared all caches, and/adminloaded normally afterwards.index.htmlfor requests that clearly aren't navigations (.js,.json,.map, …) — return a real 404 with a non-HTML content type.Happy to open a PR for (1) if you'd take it.
Environment
949ddb2d(v0.0.17 dev),bun run dev, Vite on:5173🤖 Generated with Claude Code
https://claude.ai/code/session_01FPTjUK1YJcwK83zYoANRbb