Transport modes

Choose one listener plan.

node:http2Built-in HTTPS server
0Runtime dependencies
CJS + ESMHTTPS configuration
1 pipelineShared static behavior
ModeConfigurationListenersUse it for
HTTPDefault configurationserver.serverCLI tasks, local assets, trusted networks, or TLS at an edge.
HTTPS onlyKey + certificate + https.only:trueserver.secureServerDirect encrypted local or network serving.
HTTP + HTTPSKey + certificate + https.only:falseserver.server and server.secureServerPaired listener deployments and controlled migration.

Both transports use the same roots, hooks, request-body handling, ranges, validators, compression, and logging. Routing uses HTTP/1 Host or HTTP/2 :authority. Plain HTTP remains HTTP/1.1; the HTTPS listener negotiates the protocol separately for each connection.

Interface choice: the CLI starts an HTTP listener. CommonJS and ESM carry certificate paths and HTTPS listener modes.

HTTP/2 negotiation

One HTTPS port serves both protocols.

Starting with 10.0.0, configured HTTPS uses node:http2.createSecureServer() with allowHTTP1:true. TLS ALPN selects HTTP/2 when the client supports it and HTTP/1.1 for clients that need it. This happens before HTTP requests begin, with no failed HTTP/2 request or retry. HTTP/1.1 compatibility also serves application clients and reverse proxies whose upstream connections use HTTP/1.1.

https.http2 defaults to true. Set it to false alongside the certificate configuration to select the original node:https listener when an application needs its native HTTP/1 APIs. https.only controls whether the separate plain HTTP listener starts; it does not select an HTTP version. Cleartext HTTP/2 (h2c), HTTP/2-only mode, and HTTP/3 are not provided.

Hooks receive Node's HTTP/2 compatibility request/response objects on HTTP/2 requests. Use public response methods and the supplied serve continuation. HTTP/2 uses streams, so raw HTTP/1 socket writes and connection-specific headers such as Connection and Transfer-Encoding cannot be used. Read request.httpVersionMajor when a hook needs the negotiated protocol. HTTP/2 request bodies reach the existing complete body fields even without Content-Length.

server.timeout applies to HTTP/2 session inactivity and HTTP/1 socket inactivity. requestTimeout, headersTimeout, and keepAliveTimeout apply only to HTTP/1 connections, including those negotiated on HTTPS; they do not set HTTP/2 stream deadlines.

Node.js 22.12 or newer is still required. HTTP/2 adds no runtime dependency. See Node's HTTP/2 compatibility API for the native request/response contract.

HTTPS-only · ESM

Start one encrypted listener.

ESM · HTTPS-only
import {Server} from 'node-http-server';

const server=new Server({
    root:'./public',
    host:'127.0.0.1',
    https:{
        privateKey:'./certs/private/server.key',
        certificate:'./certs/server.crt',
        port:8443,
        only:true
    }
});

server.deploy((instance,listener)=>{
    console.log('HTTPS ready',listener.address());
});

server.secureServer.once('error',error=>{
    console.error(error);
});
ResultValue
URLhttps://127.0.0.1:8443
HTTP listenerserver.server === null
HTTPS listenerserver.secureServer
address()HTTPS address

HTTP + HTTPS · CommonJS

Run paired listeners from one instance.

CommonJS · paired listeners
const {Server}=require('node-http-server');

const server=new Server({
    root:'./public',
    host:'127.0.0.1',
    port:8080,
    https:{
        privateKey:'./certs/private/server.key',
        certificate:'./certs/server.crt',
        port:8443,
        only:false
    }
});

server.deploy((instance,listener)=>{
    const protocol=listener===instance.secureServer ? 'https' : 'http';
    console.log(protocol+' ready',listener.address());
});

server.server.once('error',error=>console.error(error));
server.secureServer.once('error',error=>console.error(error));
Readiness: the deploy callback runs once for the HTTP listener and once for the HTTPS listener. The listener argument identifies which address became ready.

TLS configuration

Pass filesystem paths to trusted material.

KeyDefaultContract
https.privateKey''Required private-key path when HTTPS is enabled.
https.certificate''Required certificate path when HTTPS is enabled.
https.ca''Optional CA certificate path.
https.passphrasefalseOptional private-key passphrase.
https.port443HTTPS port; 0 requests an OS-selected free port.
https.onlyfalsetrue selects HTTPS-only; false keeps HTTP and HTTPS.
https.http2trueHTTP/2 with HTTP/1.1 negotiation; false selects the original node:https listener.

Deployment reads the configured files and constructs the selected native Node HTTPS listener before listeners open. Missing, incomplete, unreadable, or invalid material fails startup as one atomic operation.

Listener lifecycle

Inspect and close the listeners you own.

MemberHTTPS-onlyHTTP + HTTPS
server.servernullActive Node HTTP server
server.secureServerNode Http2SecureServer by defaultNode Http2SecureServer by default
server.address()HTTPS addressHTTP address
server.secureServer.address()HTTPS addressHTTPS address
await server.close()Closes HTTPSCloses both listeners

With https.http2:false, secureServer is the original Node https.Server. Listener bind failures use Node's native error event. Attach handlers to every active listener. HTTP/2 session and stream failures are captured in lastError. Repeated and concurrent close() calls share the same close operation; HTTP/2 sessions close gracefully after their active streams finish.

Certificate operations

Use material issued for the deployment.

DeploymentCertificate sourceOperator action
Local developmentLocal CA or development certificate toolingTrust the CA deliberately and match the requested hostname.
Direct production listenerLet's Encrypt or another maintained public or private CAProtect key files, monitor expiry, and redeploy after rotation.
Reverse proxy or hosting edgeEdge-managed certificateTerminate TLS at the edge and run the server on the protected upstream network.
Private keys: store them outside every served root with narrow filesystem permissions. Generate fresh material for each environment.

Let's Encrypt · Linux + Certbot

Issue a certificate and keep the listener current.

Let's Encrypt issues public certificates through ACME clients such as Certbot. Certificate issuance and renewal run outside node-http-server; the module loads the resulting PEM files through its existing HTTPS settings. If your hosting provider manages TLS, use its certificate automation at that edge.

1. Obtain a certificate for your domain

This walkthrough targets a Linux host with Certbot installed using its official instructions for other web servers. For another operating system, choose a supported client from the ACME client list. Replace example.com with a domain you control; the localhost certificate examples remain separate.

Point the domain's DNS records at the challenge host, including any published IPv6 record. HTTP-01 validation requires public inbound TCP port 80, even when the application uses HTTPS on another port. Route that traffic through any firewall, NAT, or load balancer to the challenge responder.

Standalone mode lets Certbot own port 80 during validation. The port must be free; if another service owns it, arrange its stop/start through that service's operator or choose a different challenge method.

Linux shell · initial issuance
sudo certbot certonly --standalone --cert-name example.com -d example.com
sudo certbot certificates

Follow Certbot's account prompts and use the paths it reports. For the selected certificate name, map /etc/letsencrypt/live/example.com/privkey.pem to https.privateKey and /etc/letsencrypt/live/example.com/fullchain.pem to https.certificate. Certbot updates these live paths during renewal; see certificate files.

2. Load the certificate in node-http-server

CommonJS · direct HTTPS
const {Server}=require('node-http-server');

const server=new Server({
    root:'/var/www/example/public',
    host:'0.0.0.0',
    domain:'example.com',
    https:{
        privateKey:'/etc/letsencrypt/live/example.com/privkey.pem',
        certificate:'/etc/letsencrypt/live/example.com/fullchain.pem',
        port:443,
        only:true
    }
});

server.deploy();
server.secureServer.once('error',function listenerFailed(error){
    console.error(error);
    process.exitCode=1;
});

Replace the content root and domain, and use a bind address matching your deployment; this example binds IPv4. The application account needs read access to the certificate and private key through their directories, plus permission to bind port 443. Keep the private key outside the content root. Clients connect to https://example.com; HTTPS negotiates HTTP/2 or HTTP/1.1. only:true leaves port 80 available for Certbot.

fullchain.pem supplies the leaf certificate followed by its intermediates, as required by Node's TLS cert option. Leave https.ca unset for this setup; it is not the place to supply the server's intermediate chain. The module reads files at deploy() and does not automatically watch or reload renewed certificates.

3. Renew and restart the application

Use the renewal scheduler supplied by your Certbot installation. Keep port 80 available for each standalone renewal. An operator can rehearse ACME renewal with:

Linux shell · renewal rehearsal
sudo certbot renew --cert-name example.com --dry-run

For an existing systemd application service, save the following as /etc/letsencrypt/renewal-hooks/deploy/restart-example-web and make it executable. Replace the lineage and example-web.service with the certificate path and service you own.

Executable deploy-hook file · Linux systemd
#!/bin/sh
if [ "$RENEWED_LINEAGE" = "/etc/letsencrypt/live/example.com" ]; then
    systemctl restart example-web.service
fi

Certbot runs deploy hooks after successful renewal. A dry run does not run them by default. The application's existing shutdown/startup lifecycle must load the new files; a restart can briefly interrupt service. Inspect the certificate presented by the application after deployment to confirm it loaded the renewed certificate.

If another listener must stop for standalone validation, use Certbot's pre/post hooks to stop and restore that service, including when renewal fails. The successful-renewal deploy hook cannot restore a service after failed validation.

When standalone does not fit

  • Port 80 unavailable or wildcard certificate: use automated DNS-01 validation through your DNS provider. Its challenge TXT records can be managed independently of the application listener. Manual DNS entry needs another manual step at renewal unless the client has automation hooks.
  • Existing web server on port 80: webroot validation needs /.well-known/acme-challenge/ served from its selected root. node-http-server's default server.allowDotfiles:false rejects that path. An existing edge can serve the challenge; pointing Certbot at the ordinary application root alone is insufficient.

Troubleshooting

Map the first signal to the next check.

SignalMeaningNext check
ERR_HTTPS_CONFIGURATIONKey/certificate pair is incomplete.Set both paths and confirm https.only.
Filesystem or PEM error during deployMaterial is unreadable or invalid.Paths, permissions, file format, and passphrase.
EACCES on port 443Process lacks bind permission.Use an allowed port such as 8443 or grant the deployment's intended capability.
Browser certificate warningTrust or hostname validation failed.Certificate chain, SAN hostname, local CA trust, and system clock.
Deploy callback runs twiceBoth listeners became ready.Compare the callback listener with secureServer.
address() shows the HTTP portDual mode prefers the HTTP address.Read secureServer.address() for HTTPS.
CLI output starts with http://The command owns the HTTP interface.Use the CommonJS or ESM configuration above for HTTPS.
HTTPS request uses HTTP/1.1The client negotiated HTTP/1.1, or HTTP/2 was disabled.Read request.httpVersionMajor, the client's HTTP/2 support, and https.http2.
Custom hook fails on HTTP/2The hook may use an HTTP/1-only API or header.Use the native compatibility request/response APIs and HTTP/2-compatible headers, or select https.http2:false when the application requires the original listener.
Redirect and edge policy: route HTTP-to-HTTPS redirects, certificate automation, SNI, and advanced TLS policy through application code or a maintained edge.