Skip to content

Dev server answers /sw.js with the SPA fallback as text/html, so a stale service worker can never self-heal #428

Description

@fosferon

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:

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions