Creating an HTTPS Server
HTTPS is HTTP over TLS — the encryption layer that makes the web secure. It guarantees three things: confidentiality (eavesdroppers can't read the traffic), integrity (it can't be tampered with in transit), and authentication (the client verifies it's really talking to your server). Node's https module mirrors http almost exactly; the only real addition is supplying a certificate and private key. This page covers running TLS in Node and when you should — and shouldn't.
The minimal HTTPS server
import { createServer } from 'node:https'
import { readFileSync } from 'node:fs'
const options = {
key: readFileSync('./certs/key.pem'), // private key
cert: readFileSync('./certs/cert.pem'), // certificate
}
createServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Secure hello over TLS\n')
}).listen(443, () => console.log('https://localhost'))Certificates and keys, briefly
Piece | What it is |
|---|---|
Private key | Secret key that decrypts traffic — never share it |
Certificate | Public document binding your domain to a public key |
CA (Certificate Authority) | Trusted issuer whose signature browsers trust |
Self-signed cert | You sign your own — fine for local dev, untrusted by browsers |
CA-signed cert | Signed by a trusted CA — required for production |
Local development certificates
For local testing you can generate a self-signed certificate. Browsers will warn (it's not CA-signed), which is expected on localhost:
# Quick self-signed cert (OpenSSL): openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 # Better DX: mkcert creates a locally-trusted cert (no browser warning) mkcert -install mkcert localhost
Production reality: terminate TLS upstream
Get certificates free with Let's Encrypt
For real domains, Let's Encrypt issues trusted CA-signed certificates at no cost, automated via the ACME protocol. Tools like certbot or a proxy with built-in ACME (Caddy, Traefik) obtain and auto-renew them — important because Let's Encrypt certs are valid for only 90 days.
Redirect HTTP to HTTPS
Run a tiny plain-HTTP server alongside HTTPS that 301-redirects everything to the secure URL, so visitors on http:// land on https://:
import http from 'node:http'
http.createServer((req, res) => {
const host = req.headers.host?.split(':')[0]
res.writeHead(301, { Location: `https://${host}${req.url}` })
res.end()
}).listen(80)Key takeaways
https.createServer(options, handler)=http+ akey/cert— handling is otherwise identical.Protect the private key like a credential; never commit it.
Local dev: use
mkcertfor trusted, warning-free certs.Production: usually terminate TLS at a proxy/load balancer; use Let's Encrypt with automated renewal.
Redirect HTTP→HTTPS and send HSTS to enforce secure connections.