cheat sheet
http-server
Package-level reference for http-server on npm — install, HTTPS, gzip, proxy mode, basic auth, and the serve comparison.
http-server
What it is
http-server is a small, zero-config command-line static-file server. It predates serve (2011 vs 2014), is dependency-light, and ships HTTPS, optional gzip, CORS, basic auth, and a proxy mode out of the box. The CLI surface is denser than serve's — fewer ergonomic defaults, more flag knobs.
It's the canonical answer to "I need to serve this directory over HTTP for two minutes" when you don't want Python and don't have Vite preview already wired up.
Install
# Global install (most common)
npm install -g http-server
# Per-project dev dep
npm install -D http-server
# One-off
npx http-server ./public
Output: binaries http-server and a back-compat hs alias on PATH.
Versioning & Node support
- Current major line is
14.x(released 2022). Modest changes since; very stable. - Recent releases require Node 14+ (older than
serve's Node-18 floor — useful for legacy CI). - Always a dev dependency; never ships in runtime artefacts.
Package metadata
- Maintainer: http-party (community); origins with Charlie Robbins (Nodejitsu).
- Project home: github.com/http-party/http-server
- npm: npmjs.com/package/http-server
- License: MIT
- First released: 2011 — one of the oldest server utilities on npm.
- Downloads: ~2-4 million per week. Lower than
servebut still significant. Many older tutorials reach for it first.
Peer dependencies & extras
http-server has a small dependency tree (portfinder, union, mime, secure-compare, basic-auth, corser, opener, htpasswd). No peer deps, no companion packages needed.
| Adjacent tool | Purpose |
|---|---|
mkcert | Generate trusted-locally TLS certs to pair with --ssl |
localtunnel / ngrok | Expose your http-server to the public internet for quick demos |
nodemon | Restart http-server on config change — rarely needed since it has no config file |
apache2-utils (htpasswd) | Generate the htpasswd file --proxy and basic-auth flags consume |
Alternatives
| Tool | Trade-off |
|---|---|
serve | Vercel's competitor — better SPA-mode UX, nicer banner output, but lacks built-in basic-auth and proxy mode. |
python -m http.server | Stdlib in Python. Zero deps. No HTTPS, no compression. |
bun --bun serve | Bun's built-in static server — faster, requires Bun. |
| Caddy | Production server. caddy file-server is a one-liner equivalent with auto-HTTPS. |
local-web-server | Richer feature set (SPA, proxy, mocks). Larger install. |
vite preview | Built into Vite. Use this if Vite is already in the project. |
Common gotchas
- Defaults to port 8080, not 3000.
http-serverpredates the React/Next ecosystem; the port 8080 default is from the Jakarta/Tomcat era. Override with-p PORT. - No SPA mode by default. Use
--proxy http://localhost:8080?or the-P(proxy) flag pointing at a fallback URL — clumsier thanserve -s. - CORS is opt-in. Add
--cors(permissive*) or--cors='https://example.com'for a specific origin. - HTTPS requires explicit cert/key paths. No auto-cert generation; pair with
mkcertoropenssl req. - Basic auth uses an
.htpasswdfile — not inline username/password. Generate withhtpasswd -c .htpasswd user. - Default opens the browser.
-oopens automatically;-o /pathopens to a specific path. Disable with no-o. - Directory listings are HTML, not JSON. No API mode; for programmatic use, pair with a different server or write a tiny Node script.
- Compression (
-g/--gzip) only sends pre-compressed files. It looks forpath.gzsiblings, not on-the-fly gzip. For on-the-fly compression, use a different server.
Real-world recipes
Basic static directory serve
npx http-server ./dist
Output:
Starting up http-server, serving ./dist
http-server version: 14.1.1
Available on:
http://127.0.0.1:8080
http://192.168.1.5:8080
Hit CTRL-C to stop the server
HTTPS with self-signed cert
# Generate a cert (use mkcert for trusted local)
mkcert localhost
# Serve over HTTPS
npx http-server -S -C localhost.pem -K localhost-key.pem ./dist
Output:
Created a new certificate valid for localhost
Starting up http-server, serving ./dist through https
Available on:
https://127.0.0.1:8080
Hit CTRL-C to stop the server
Or with one-shot OpenSSL:
openssl req -newkey rsa:2048 -new -nodes -x509 -days 365 \
-keyout key.pem -out cert.pem -subj "/CN=localhost"
npx http-server -S -C cert.pem -K key.pem ./dist
Output:
Generating a RSA private key
...+++++
writing new private key to 'key.pem'
Starting up http-server, serving ./dist through https
Available on:
https://127.0.0.1:8080
-S (or --ssl) enables HTTPS; -C is the cert, -K is the key.
Gzip and brotli (pre-compressed files)
npx http-server ./dist -g -b
Output:
Starting up http-server, serving ./dist
Available on:
http://127.0.0.1:8080
Hit CTRL-C to stop the server
-g / --gzip and -b / --brotli serve file.gz / file.br siblings when the client sends Accept-Encoding: gzip/br. Files must be pre-compressed by the build step:
# vite plugin example — vite-plugin-compression
# or post-build:
find dist -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" \) \
-exec gzip -k -9 {} \;
npx http-server dist -g
Output: (none — exits 0 on success)
Basic authentication
# Generate htpasswd (or write your own)
htpasswd -c .htpasswd alice
# (prompts for password)
# Serve with basic auth — flag varies by version; recent versions:
npx http-server ./dist --username alice --password hunter2
Output:
New password:
Re-type new password:
Adding password for user alice
Starting up http-server, serving ./dist
Available on:
http://127.0.0.1:8080
The flag-based auth is convenient but exposes credentials in ps. Prefer .htpasswd files or front with Caddy/nginx for any real protection.
CORS
# Permissive — useful for fetch testing
npx http-server ./dist --cors
# Restrict to a specific origin
npx http-server ./dist --cors='https://app.example.com'
Output:
Starting up http-server, serving ./dist
Available on:
http://127.0.0.1:8080
Hit CTRL-C to stop the server
Proxy fallback (for SPA support)
http-server does SPA fallback through proxy mode: if the file doesn't exist, proxy the request to a backend (typically the same server pointing at itself with ?fallback=index.html).
npx http-server ./dist --proxy http://localhost:8080?
Output:
Starting up http-server, serving ./dist
Unhandled requests will be served from: http://localhost:8080?
Available on:
http://127.0.0.1:8080
The trailing ? is meaningful — it's a "proxy on 404" sentinel. Combined with the same http-server instance, this effectively rewrites 404s to index.html (which then handles client-side routing).
In practice this is awkward; if you need SPA support, serve -s is cleaner.
Custom MIME types
http-server reads mime.types files via the mime package. Override with --mimetypes <path>:
echo "application/wasm wasm" > my-mime.types
npx http-server ./dist --mimetypes my-mime.types
Output:
Starting up http-server, serving ./dist
Available on:
http://127.0.0.1:8080
Hit CTRL-C to stop the server
Run in background for tests
# Start in background
npx http-server ./dist -p 5001 -s & # -s suppresses logs
SERVER_PID=$!
# Wait for ready
npx wait-on http://localhost:5001
# Run tests
npx playwright test
# Tear down
kill $SERVER_PID
Output:
Running 12 tests using 4 workers
12 passed (8.4s)
Production deployment
Same caveats as serve — http-server is a dev tool. For production:
- Caddy (single binary, auto-HTTPS, file-server module).
- nginx (classic, fast, more config required).
- Hosted static (Vercel, Netlify, Cloudflare Pages, S3+CloudFront, etc.).
http-server lacks: HTTP/2 (until recent versions, support is experimental), HTTP/3, graceful shutdown / hot reload, ACME integration, comprehensive log rotation, supervisor integration.
Performance tuning
Minimal knobs:
-g/-bfor pre-compressed assets. Saves bandwidth and CPU vs on-the-fly compression.- Disable directory listing (
-d false) — small CPU saving on directory walks for index pages. -c-1disables ETag andCache-Control— useful for "always fresh" testing; default is-c3600(1-hour cache). For production-like behaviour set-c31536000(1 year, immutable assets).
For throughput >1k req/s, switch to Caddy or nginx — Node's single-threaded http is the bottleneck.
Version migration guide
http-server has been remarkably stable since v0.9. Major version bumps:
| From → To | Highlights |
|---|---|
| 0.x → 14.x | Skipped many version numbers; the 14.0 was largely cosmetic. Flag names stable. |
| 14.0 → 14.1 | Security patch for path traversal. Update mandatory. |
Migration to serve
# Was
npx http-server -p 5000 ./dist
# Now (similar UX, different defaults)
npx serve -l 5000 dist
Output:
┌──────────────────────────────────────┐
│ Serving! │
│ Local: http://localhost:5000 │
└──────────────────────────────────────┘
For SPA fallback, serve -s is the equivalent of the proxy-based hack.
Security considerations
- Path traversal patches. Older versions (< 14.1) had CVEs allowing
../-style escapes. Always pin to a patched release. - Default binds to all interfaces.
0.0.0.0:8080— visible on LAN. Use-a 127.0.0.1for local-only. - No authentication by default. Use
--username/--password, or front with Caddy / nginx / Cloudflare Access. Don't expose unauthenticatedhttp-serverto the internet. - Self-signed HTTPS doesn't validate hosts. Browsers will warn;
curl --insecuremasks issues. For trusted local cert, usemkcert. - Logs may include query strings. If your app puts tokens in URLs, log redaction is your problem —
http-serverdoesn't do it.
Configuration patterns
http-server has no config file — all options are CLI flags. For repeatability, encode them in package.json scripts:
{
"scripts": {
"preview": "http-server dist -p 5000 -c-1 --cors -g",
"preview:https": "http-server dist -p 5443 -S -C cert.pem -K key.pem"
}
}
Common flag combos:
| Use case | Flags |
|---|---|
| Local dev preview | -p 3000 -c-1 (no cache) |
| Production-parity check | -c31536000 -g -b (year cache, pre-compressed) |
| Local-only | -a 127.0.0.1 |
| LAN demo | -p 8080 (default) — visible to whole network |
| With CORS | --cors |
| HTTPS | -S -C cert.pem -K key.pem |
Troubleshooting common errors
EADDRINUSE— port 8080 collision (common). Use-p 8081orlsof -i :8080.- HTTPS doesn't load — self-signed cert isn't trusted. Use
mkcert -installonce; recreate cert; restart. - CORS errors after adding
--cors— verify the response actually hasAccess-Control-Allow-Origin(curl -Ithe URL). Some browsers cache CORS failures aggressively; clear site data. - Pre-compressed files not served — confirm both the
.gz/.brfile exists AND the client sentAccept-Encoding: gzip,br. Browsers do this automatically; rawcurldoes not. - Directory listing shows when you expected
index.html— confirmindex.htmlis in the served root, not a subdirectory.
Ecosystem integrations
- CI smoke tests — pair with
wait-onandplaywright test/cypress runfor "build → serve → e2e" loops. - Docker — minimal:
FROM node:20-alpine; RUN npm install -g http-server; COPY dist /app; CMD ["http-server", "/app", "-p", "8080"]. live-server—http-server's cousin with live-reload. Same maintainers (community). Addlive-serverwhen you want auto-reload on file change.
When NOT to use this
- Production hosting. Use Caddy, nginx, or a CDN-backed static host.
- You need SPA fallback.
serve -sis the cleaner UX;http-server's proxy hack is awkward. - You need on-the-fly compression.
http-server's gzip only sends pre-compressed siblings. Caddy compresses dynamically. - You already use Vite / Next / Astro. Their built-in preview commands match the build output exactly — no chance of MIME-type drift.
- You want HTTP/2 or HTTP/3. Not supported (HTTP/2 is experimental as of recent versions).
- You're hosting a single file briefly.
python -m http.serverorbun --bun serveare smaller-footprint.
http-server is the right tool when you need basic-auth or HTTPS in a one-shot static-serve scenario without writing code. For the simpler SPA-preview case, serve -s is more pleasant.
See also
- Packages: npm-serve — sibling static server with nicer SPA UX
- Concept: HTTP — request/response semantics, headers, status codes