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

bash
# 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 serve but 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 toolPurpose
mkcertGenerate trusted-locally TLS certs to pair with --ssl
localtunnel / ngrokExpose your http-server to the public internet for quick demos
nodemonRestart 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

ToolTrade-off
serveVercel's competitor — better SPA-mode UX, nicer banner output, but lacks built-in basic-auth and proxy mode.
python -m http.serverStdlib in Python. Zero deps. No HTTPS, no compression.
bun --bun serveBun's built-in static server — faster, requires Bun.
CaddyProduction server. caddy file-server is a one-liner equivalent with auto-HTTPS.
local-web-serverRicher feature set (SPA, proxy, mocks). Larger install.
vite previewBuilt into Vite. Use this if Vite is already in the project.

Common gotchas

  1. Defaults to port 8080, not 3000. http-server predates the React/Next ecosystem; the port 8080 default is from the Jakarta/Tomcat era. Override with -p PORT.
  2. No SPA mode by default. Use --proxy http://localhost:8080? or the -P (proxy) flag pointing at a fallback URL — clumsier than serve -s.
  3. CORS is opt-in. Add --cors (permissive *) or --cors='https://example.com' for a specific origin.
  4. HTTPS requires explicit cert/key paths. No auto-cert generation; pair with mkcert or openssl req.
  5. Basic auth uses an .htpasswd file — not inline username/password. Generate with htpasswd -c .htpasswd user.
  6. Default opens the browser. -o opens automatically; -o /path opens to a specific path. Disable with no -o.
  7. Directory listings are HTML, not JSON. No API mode; for programmatic use, pair with a different server or write a tiny Node script.
  8. Compression (-g / --gzip) only sends pre-compressed files. It looks for path.gz siblings, not on-the-fly gzip. For on-the-fly compression, use a different server.

Real-world recipes

Basic static directory serve

bash
npx http-server ./dist

Output:

text
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

bash
# 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:

text
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:

bash
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:

text
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)

bash
npx http-server ./dist -g -b

Output:

text
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:

bash
# 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

bash
# 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:

text
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

bash
# 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:

text
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).

bash
npx http-server ./dist --proxy http://localhost:8080?

Output:

text
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>:

bash
echo "application/wasm wasm" > my-mime.types
npx http-server ./dist --mimetypes my-mime.types

Output:

text
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

bash
# 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:

text
Running 12 tests using 4 workers
  12 passed (8.4s)

Production deployment

Same caveats as servehttp-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 / -b for 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-1 disables ETag and Cache-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 → ToHighlights
0.x → 14.xSkipped many version numbers; the 14.0 was largely cosmetic. Flag names stable.
14.0 → 14.1Security patch for path traversal. Update mandatory.

Migration to serve

bash
# Was
npx http-server -p 5000 ./dist

# Now (similar UX, different defaults)
npx serve -l 5000 dist

Output:

text
   ┌──────────────────────────────────────┐
   │  Serving!                            │
   │  Local: http://localhost:5000        │
   └──────────────────────────────────────┘

For SPA fallback, serve -s is the equivalent of the proxy-based hack.

Security considerations

  1. Path traversal patches. Older versions (< 14.1) had CVEs allowing ../-style escapes. Always pin to a patched release.
  2. Default binds to all interfaces. 0.0.0.0:8080 — visible on LAN. Use -a 127.0.0.1 for local-only.
  3. No authentication by default. Use --username/--password, or front with Caddy / nginx / Cloudflare Access. Don't expose unauthenticated http-server to the internet.
  4. Self-signed HTTPS doesn't validate hosts. Browsers will warn; curl --insecure masks issues. For trusted local cert, use mkcert.
  5. Logs may include query strings. If your app puts tokens in URLs, log redaction is your problem — http-server doesn't do it.

Configuration patterns

http-server has no config file — all options are CLI flags. For repeatability, encode them in package.json scripts:

json
{
  "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 caseFlags
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 8081 or lsof -i :8080.
  • HTTPS doesn't load — self-signed cert isn't trusted. Use mkcert -install once; recreate cert; restart.
  • CORS errors after adding --cors — verify the response actually has Access-Control-Allow-Origin (curl -I the URL). Some browsers cache CORS failures aggressively; clear site data.
  • Pre-compressed files not served — confirm both the .gz/.br file exists AND the client sent Accept-Encoding: gzip,br. Browsers do this automatically; raw curl does not.
  • Directory listing shows when you expected index.html — confirm index.html is in the served root, not a subdirectory.

Ecosystem integrations

  • CI smoke tests — pair with wait-on and playwright test / cypress run for "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-serverhttp-server's cousin with live-reload. Same maintainers (community). Add live-server when 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 -s is 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.server or bun --bun serve are 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