One file. Zero dependencies.
A Node web server for a single-page app that does what a static host does — safe paths, brotli, caching, SPA fallback, a PWA — and scores 100 in every Lighthouse category. Nothing to install: one command serves any folder.
npx @labs_kai/server dist
# → http://127.0.0.1:2211
Ask this server something
Each answer comes live from the server hosting this page. The line under the status is what simple-server answers, so here the two should agree.This copy is on a static host, so the answers below are the host's. The line under the status is what simple-server answers when you run npm start.
– ms
200 OK
With npm start: 200, the app shell
- content-type
- text/html; charset=utf-8
- cache-control
- no-cache
- content-encoding
- br
- vary
- Accept-Encoding
- etag
- W/"…"
- content-length
- x-content-type-options
- nosniff
- x-frame-options
- SAMEORIGIN
- allow
- location
- server
What a static host does.
Everything a good static host does and a bare http.createServer doesn't. Two hundred lines, read in ten minutes: lib/simple-server.js. Its tests run over a real socket: npm test.
- Safe paths
- Every URL resolves inside
public/.../, encoded dots, doubled slashes and dotfiles can't escape it;/.well-known/can. - SPA fallback
- Extension-less paths get
index.htmlwith a 200, so client-side routing works. Unknown/api/*paths and non-HTML requests get a real 404. - Caching that doesn't bite
- HTML,
sw.jsand the manifest:no-cache. Assets: a day. Fingerprinted assets: a year,immutable. Weak ETags andLast-Modifiedturn repeat visits into 304s. - Compression
- Brotli or gzip for text over 1 KB, with
Vary: Accept-Encoding. Images and fonts are left alone. - Right types
- Content-Type with charset for text,
application/manifest+json,font/woff2,image/x-icon,application/wasm. - Range requests
<video>and<audio>seek with206 Partial Content.- Security headers
X-Content-Type-Options,Referrer-Policy,X-Frame-Options; HSTS when the connection is TLS.- Never a crash
- Malformed URLs are a 400, other methods a 405, a file that disappears mid-request a 404, anything unexpected a 500. The process stays up.
- Health and routes
GET /api/healthanswers with uptime and request count; add your own JSON routes in a line each.- HTTPS when you want it
npm run certmakes a localhost pair and the server switches to HTTPS. Behind nginx or Caddy, leave it off.
Measured in your browser.
Not a claim: this page load, read from the Performance API and rendered by minim. It holds on any host.
– KB
over the wire for the whole page: – KB of HTML, – smaller compressed (the service worker answered this visit, so these sizes are from a fresh request).
- JS, CSS and font requests
- 0
- First paint
- –
- Protocol
- –
And from the process serving it, via /api/health every ten seconds:
- Requests served
- –
- Uptime
- –
- Node
- –
A PWA that updates itself.
The build hashes every file it ships into the service worker, so each deploy is a new cache version. The shell, the not-found page and the icons are precached at install; assets are served from cache and refreshed in the background; a navigation that can't reach the network gets the shell, so the app opens offline.
When a new version is installed behind the page, it waits. The page shows a prompt, and one tap swaps the worker and reloads. Because the server sends sw.js and the manifest with no-cache, browsers see a deploy at once instead of a day later.
The manifest has an id, maskable icons, a shortcut and screenshots, so it installs cleanly on Android, desktop Chrome, Edge and Safari.
This install
- Service worker
- checking
- Cache version
- –
- Network
- online
- Installed as an app
- no
A new version is ready.
Make it yours.
Your app
Drop your build into public/. index.html is the shell every client-side route falls back to, 404.html the not-found page. Fingerprint your assets (app.3f9c2a1b.js) and they are cached for a year. Everything else is an environment variable.
PORT=8080 HOST=0.0.0.0 npm start
npm run dev # verbose, restarts on change
npm run cert # then npm start speaks https
npm run build # site/ → public/ (this page, the service worker)
Your agent
The repository is laid out for coding agents as much as for people. AGENTS.md explains the layout, the rules and the commands; skills/simple-server teaches an agent to build and verify a page the way this one was built; npm run check is the whole verification — tests, a static preflight of the pages, Lighthouse against the real server. And the server speaks MCP: started with MCP=1, an agent can list and read what it serves and see exactly how it answers any path.
npx skills add hertonwork/server
npm run check # tests + preflight (+ Lighthouse)
MCP=1 npm start
claude mcp add --transport http server \
http://127.0.0.1:2211/mcp