server

A static web server for a single-page app, in one Node file, with nothing to install.

Lighthouse: 100 Performance, 100 Accessibility, 100 Best Practices, 100 SEO — with fireworks

Live at server.herton.work — that copy is GitHub Pages serving public/, so the live-status panel says so; clone it and npm start to see the server itself answer.

Run it

git clone https://github.com/hertonwork/server.git
cd server
npm start            # → http://127.0.0.1:2211

Put your app in public/. index.html is the shell every client-side route falls back to, 404.html the not-found page. Everything else is an environment variable:

Variable Default What it does
PORT 2211 Port to listen on
HOST 127.0.0.1 Bind address. 0.0.0.0 in a container or to reach it from another machine
PUBLIC_DIR public Folder to serve
VERBOSE off VERBOSE=1 logs one line per request
TLS_KEY, TLS_CERT key.pem, cert.pem When both files exist the server speaks HTTPS
PORT=8080 HOST=0.0.0.0 npm start
npm run dev          # verbose, restarts when a file changes
npm run cert         # self-signed localhost certificate → the next `npm start` is https
npm test             # the server's tests (node:test, over a real socket)
npm run check        # tests + build + preflight + Lighthouse against this server

Behind nginx or Caddy you don’t need the certificate: the proxy terminates TLS and talks plain HTTP to 127.0.0.1:2211.

location / {
    proxy_pass http://127.0.0.1:2211;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}

What the server does

   
Safe paths Every URL is resolved inside public/; ../, encoded dots and doubled slashes can’t escape it. Dotfiles (.env, .git) are hidden, /.well-known/ is not
SPA fallback Extension-less paths (/about, /users/42) get index.html with a 200 so client-side routing works. Unknown /api/* paths and non-HTML requests get a real 404
Correct types Content-Type with charset for text, image/x-icon, font/woff2, application/manifest+json, application/wasm, …
Caching HTML, sw.js and the manifest: no-cache (always revalidated, so deploys show up at once). Assets: one day. Fingerprinted assets (app.3f9c2a1b.js): one year, immutable. Weak ETags and Last-Modified → 304s
Compression Brotli or gzip for text over 1 KB, Vary: Accept-Encoding
Range requests <video> and <audio> seek with 206 Partial Content
Security headers X-Content-Type-Options, Referrer-Policy, X-Frame-Options; HSTS when the connection is TLS
Robust Malformed URLs → 400, POST → 405, a file that disappears → 404, an unexpected error → 500. Never a crash
Health GET /api/health → { ok, started, uptime, requests, node }

Use it as a module

import { createServer } from './lib/simple-server.js';

createServer({
  publicDir: 'dist',
  maxAge: 3600,
  headers: { 'Permissions-Policy': 'camera=()', 'X-Frame-Options': null },   // add one, drop one
  routes: {
    'GET /api/time': () => ({ now: Date.now() }),                           // return an object → JSON
    'POST /api/echo': (req, res) => { req.pipe(res); },                     // or write the response yourself
  },
}).listen(2211);

createHandler(options) returns just the (req, res) function if you want to mount it on your own server. All options and their defaults are at the top of lib/simple-server.js.

The page

public/index.html is the server’s demo and the shell your app replaces: one HTML file with no request for JS, CSS or fonts. It is built from site/ with sscss (a vocabulary of tiny utility classes; the build generates exactly the rules the page uses, 2.5 KB) and Minim (a 2.5 KB reactive library; the state is a JSON tag, the markup binds to it by key). On it:

npm run build        # site/ → public/index.html, public/404.html, public/sw.js
npm run check        # + preflight + Lighthouse (npm i -g lighthouse; CHROME_PATH if Chrome isn't found)
npm run vendor       # refresh vendor/ (Minim, the sscss generator, the preflight) from their repositories
npm run shots        # manifest screenshots + preview.jpg from the running server (Playwright)
npm run fireworks    # the Lighthouse report's fireworks as public/lighthouse.gif (Playwright + ImageMagick)

Every push to main publishes public/ to GitHub Pages (.github/workflows/pages.yml).

The PWA

For agents

The repository is laid out to be worked on by coding agents as much as by people:

License

MIT