Serve local HTTPS

Run a Node.js HTTPS server

Load the lab certificate and key into a minimal local HTTPS server bound to loopback.

Time to put the lab certificate to work. An HTTPS server is an application listener plus a certificate and its matching private key. Node.js ships all of it in the standard library, so we need zero dependencies.

Create server.mjs in the folder that holds app.key and app.crt:

import https from 'node:https'
import fs from 'node:fs'

https.createServer({
  key: fs.readFileSync('app.key'),
  cert: fs.readFileSync('app.crt'),
}, (request, response) => {
  response.end('secure lab\n')
}).listen(8443, '127.0.0.1')

The options object is where TLS happens. key is the server’s private key. cert is the certificate we issued. The second argument to listen binds the server to 127.0.0.1 only. Keep the first lab on loopback: nothing outside your machine can reach it, which is what you want while practicing.

Run it:

node server.mjs

No output means it started. Let’s confirm port 8443 is bound to loopback only:

lsof -nP -iTCP:8443 -sTCP:LISTEN
# COMMAND   PID   USER   FD   TYPE  DEVICE  SIZE/OFF  NODE  NAME
# node    70632 flavio  14u   IPv4  0x9687...  0t0    TCP   127.0.0.1:8443 (LISTEN)

127.0.0.1:8443 in the NAME column confirms the loopback bind. If you saw *:8443 instead, the server would be listening on every interface. The -nP flags keep addresses and ports numeric. Without -P, lsof prints the port as pcsync-https, which is the service name registered for 8443 and not what you want to read.

Now probe it from a second terminal:

curl https://127.0.0.1:8443/
# curl: (60) SSL certificate problem: unable to get local issuer certificate

That error is correct behavior. curl doesn’t trust your lab CA, so verification fails. The next lessons fix this the right way, by giving curl the hostname and the root. Not by turning verification off.

One failure worth knowing. If key and cert don’t belong together, Node refuses to start:

Error: error:05800074:x509 certificate routines::key values mismatch

Node won’t serve a broken identity. When you see this, you loaded a key from one generation and a certificate from another. Re-check which files you copied. There’s a lesson later on how to prove which key matches which certificate without exposing either.

Before moving on, run chmod 600 app.key if you haven’t, and keep the practice server on your own computer. It’s a lab, not a deployment.

Lesson completed