Before network exposure

Start local, then change one boundary at a time.

  1. Keep loopback for local tools.The default host:'127.0.0.1' accepts connections only from this machine.
  2. Set an external bind deliberately.host:'0.0.0.0' or --host 0.0.0.0 listens on IPv4 interfaces. Connect through a real interface address; 0.0.0.0 is not a browser destination.
  3. Apply network controls.Configure firewall, container, VM, or platform ingress to expose only the intended port and sources.
  4. Put edge controls at the proxy.node-http-server focuses on static serving and Host/root routing. Add authentication, authorization, rate limits, WAF policy, and certificate automation at the reverse proxy or hosting edge.
  5. Set body/time protections.Choose finite request limits and timeouts for expected traffic. The body cap is unlimited until configured.
  6. Review the served root.Keep secrets, source maps, private keys, logs, and unrelated files outside every configured root.
  7. Keep dotfiles blocked.The default rejects every dot-prefixed segment. Set allowDotfiles:true only after auditing the complete dedicated root.
  8. Protect logs and TLS keys.Restrict filesystem access and never serve their directories.
Host routing scope: domain/domains select allowed Host values and roots. Authenticate clients at the application or edge, configure DNS in the zone, and terminate TLS through https or the proxy.

Static HTTP contract

Expected request behavior.

RequestResultOperational note
GETStreams the selected file.A custom beforeServe buffers file bodies and bypasses automatic compression.
HEADFull-GET status/headers, no body.Range is ignored.
Other method405 plus Allow: GET, HEAD.Hooks run before static fallback; body collection may produce 413 first.
Matching ETag/date validator304.Weak ETag and Last-Modified are generated for files.
One satisfiable GET byte range206.Range responses are not compressed.
Valid unsatisfiable range416.Includes unsatisfied content-range information.
Malformed/unsupported/multi-rangeIgnored; full 200.Only a single byte range is supported.
Directory pathConfigured index or 404.No directory listing.
Traversal/symlink/ADS escapeDenied.Decoded paths and real paths remain inside the selected root.
Dot-prefixed path segment403 by default.Encoded, nested, index, fallback, and real symlink targets are checked; literal allowDotfiles:true opts in globally for that server.

Failure map

Translate status or process errors into the next check.

SignalMeaningNext check
400Malformed request/URL.Request syntax and encoded path.
403Traversal, dotfile policy, outside-root real path, ADS, or restricted extension.Requested path, server.allowDotfiles, symlink target, and restrictedType.
404Missing file/index or SPA conditions not met.Root, index, extension, and Accept header.
405Static fallback supports only GET/HEAD.Use a hook for application methods.
413Body exceeded the configured cap.server.maxRequestBodyBytes.
415Extension MIME entry is explicitly false.contentType map.
416Valid byte range is unsatisfiable.Requested range and file length.
421Host did not match primary/domain mapping.domain, domains, and request Host.
500Sanitized request/stream/filesystem failure.lastError, logs, filesystem permissions; do not expose raw error text.
EADDRINUSEPort already owned.Stop conflict, choose another port, or use port 0.
EACCESBind not permitted.Use an allowed address/port and check runtime permissions.
Process stability: request-level 4xx/5xx and asynchronous log-write errors do not stop the process. CLI parse errors exit 2; deployment/listener/shutdown failures exit 1.

Clean shutdown

Stop accepting work before process exit.

ESM · close HTTP and HTTPS
import {Server} from 'node-http-server';

const server=new Server({
    root:'./public',
    host:'127.0.0.1',
    port:8080
}).deploy();

for(const signal of ['SIGINT','SIGTERM']){
    process.once(signal,async()=>{
        await server.close();
    });
}

The CLI handles SIGINT (exit 130) and SIGTERM (exit 0) after closing owned listeners. Library applications decide their own signal policy. Repeated/concurrent close() calls are safe and share in-flight close work.

Listener errors: attach Node error listeners to both server.server and server.secureServer when both protocols run. Listener failures are not stored in lastError.

Request logs

Keep records useful and protected.

BehaviorContract
FormatOne JSON object per line with timestamp.
Redactionauthorization, cookie, proxy-authorization, set-cookie, and x-api-key are replaced.
BodyExcluded unless logBody:true.
Write failureCaptured in lastError; default serving continues. CLI shows it only with verbose output.
DestinationParent directory must exist and be writable; use restrictive permissions.

Redaction does not make the entire record harmless. URLs, addresses, custom headers, and deliberately logged bodies can still contain sensitive data.

Certificates

Generate local material outside the served root.

  1. Create a local certificate directory.Keep the private subdirectory ignored and outside every static root.
  2. Generate a new private key and certificate.Use the local certificate instructions for development, or your deployment's certificate authority for production.
  3. Pass paths through the module API.The CLI does not accept HTTPS configuration.
  4. Protect and rotate.Use restrictive permissions and replace compromised or expired keys.
Old fixtures are untrusted: v8 certificate/key fixtures were removed from the tracked tree. Any copies or values recoverable from history are public and must never be used.

Migrating from v8

Handle the deliberate changes first.

Checkv9 action
RuntimeUse Node.js 22.12 or newer.
Network bindDefault changed to 127.0.0.1; set host explicitly for external traffic.
Module systemKeep CommonJS or use ESM; default singleton and named constructors are available in both.
Multiple serversCreate isolated new Server(config) instances.
ShutdownAwait Promise-based close(); redeployment is supported afterward.
Request bytesUse request.bodyBuffer for original bytes; request.body remains UTF-8 text.
Static HTTPReview streaming, HEAD, range, ETag, Last-Modified, method, SPA, and dotfile behavior.
DotfilesKeep the default denial or explicitly audit every root before allowDotfiles:true.
MIMEUnknown types now use octet-stream; overlay the built-in map, reject one type with false, or set contentType:false.
CLI parsingConfig no longer reads process arguments; use the CLI entry or pass objects.
CertificatesGenerate new keys; never use old fixture material.

Security reporting

Report privately when possible.

Use the repository's private vulnerability reporting channel when it is available. Otherwise contact the maintainer without publishing exploit details, keys, credentials, or sensitive logs in a public issue.