Every time a URL starts with https:// instead of http://, TLS (Transport Layer Security, the modern successor to the older SSL) is what's doing the work behind that extra 's' — encrypting the connection so nobody intercepting the traffic in between can read or tamper with it. Node.js's tls module lets you build servers and clients that speak this protocol directly.
What TLS actually provides
- Encryption — data traveling between client and server is scrambled in a way only the two endpoints can unscramble.
- Authentication — a certificate proves the server is genuinely who it claims to be, not an impostor intercepting the connection.
- Integrity — any tampering with data in transit is detectable.
Generating a certificate for local testing
A real, publicly trusted certificate comes from a certificate authority, but for local development, a self-signed one works fine:
openssl req -nodes -new -x509 -keyout key.pem -out cert.pem -days 365
This produces two files: key.pem (the private key — never share this) and cert.pem (the certificate, which can be shared).
A basic TLS server
const tls = require('tls');
const fs = require('fs');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
const server = tls.createServer(options, (socket) => {
console.log('Client connected securely');
socket.write('Welcome to a TLS-secured connection\n');
socket.on('data', (data) => {
console.log('Received:', data.toString());
});
});
server.listen(8000, () => {
console.log('TLS server listening on port 8000');
});
A basic TLS client
const tls = require('tls');
const fs = require('fs');
const options = {
ca: fs.readFileSync('cert.pem') // trust this specific self-signed certificate
};
const socket = tls.connect(8000, 'localhost', options, () => {
console.log('Connected. Authorized:', socket.authorized);
socket.write('Hello from the client');
});
socket.on('data', (data) => {
console.log('Server says:', data.toString());
});
How this relates to https
In practice, most applications don't use raw tls directly for web traffic — they use the https module (which wraps http with TLS underneath) instead:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Served securely over HTTPS');
}).listen(443);
Where this actually happens in production
Most real-world deployments don't handle certificates inside the Node.js application at all — a reverse proxy (Nginx) or a managed platform terminates TLS at the edge, forwarding plain HTTP internally to the Node.js process behind it. Certificates from a free, automated service (Let's Encrypt is the most common) handle renewal automatically, which avoids the very real, very common outage caused by a manually-managed certificate quietly expiring. Knowing tls and https directly matters most for understanding what's happening under the hood, and for the less common case where you genuinely need to terminate TLS inside your own Node.js process.