Serving an HTML file locally
This is the right first move when a report renders blank, and the wrong last move when someone else needs to read it. Both halves matter.
The commands
From the directory that contains index.html:
python3 -m http.server 8000 # stdlib, nothing to install
npx serve . # Node
npx http-server -p 8000 # Node, older and still fine
php -S localhost:8000 # if PHP is what you have
Open http://localhost:8000. If there's no index.html in that
directory you'll get a file listing instead of the report — that's the
server telling you that you're one level too high.
Why this fixes the blank page
A file opened by double-clicking has a file:// URL, and browsers give
those an opaque origin. Under the same-origin rules that means:
fetch()andXMLHttpRequestto a neighbouring file are blocked.<script type="module">won't load — module fetches follow CORS even for local files.
Any report that ships its results as a separate JSON file and loads them
at render time is structurally unopenable that way. Over http:// the
same requests are same-origin and go through untouched. Longer
diagnostic list in
why my HTML report lost its CSS.
What it does not fix
Nobody else can open it. localhost is their machine. Sending the
URL sends them to a connection-refused page.
Your LAN is not the reviewer's LAN. python -m http.server binds
0.0.0.0 by default, so a colleague at the same office can reach
http://192.168.x.x:8000 while it's running. Add --bind 127.0.0.1 if
you'd rather it didn't. Either way it's useless for anyone remote, on a
phone, or reading it tomorrow.
There's no HTTPS, no auth, and no lifetime. The stdlib server has no access control of any kind, and the whole thing ends when you press Ctrl-C or the laptop sleeps.
The tunnel option, and its real cost
cloudflared tunnel --url http://localhost:8000
ngrok http 8000
Both hand you a public hostname in a few seconds. Reasonable for a demo you are actively on a call for. For a report someone opens later:
- The URL dies with the process — and it's a different URL next time.
- Free tiers insert an interstitial page and rotate the hostname.
- Access control is "the hostname is hard to guess", which is not access control. Anyone the link reaches, and anything that scrapes it, is in.
- You've published a port on your working machine to the internet for the duration.
When to just publish it
If the answer to "who reads this, and when" is anyone other than you, right now, the local server has done its job and should be replaced by a URL. Drag the directory (or a zip of it) into Comma:
index.htmlbecomes the report body and the sibling assets upload with it — relative references rewritten to the uploaded copies, which is the same problem the local server was solving, solved permanently.- Scripts still run, sandboxed, so the interactive parts stay interactive — interactive HTML reports.
- Access is per report: private, team, anyone signed in, or link holders with view, comment or edit. See sharing & access control.
- Comments anchor to the content, so the reply comes back on the report rather than in a thread three days later — commenting on HTML.
For the CI version of the same move — generate, publish, keep one URL across runs — see publishing from CI.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.