node:https
require('https') The node:https module provides HTTPS server and client functionality in Node.js. It’s similar to the http module but adds TLS/SSL encryption for secure communications. This module is essential for building secure web servers and making encrypted API requests.
Common Usage
Creating an HTTPS Server
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert')
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Secure Hello, World!');
});
server.listen(443, () => {
console.log('HTTPS Server running on https://localhost');
});
The server example shows how to create a TLS-enabled listener. For the client side, https.get() wraps https.request() with the GET method, making it the simplest way to fetch data from a secure endpoint. The response is a readable stream — you listen for data and end events rather than buffering the entire payload.
Making HTTPS GET Requests
const https = require('https');
const req = https.get('https://example.com', (res) => {
console.log(`STATUS: ${res.statusCode}`);
res.on('data', (chunk) => {
console.log(`DATA: ${chunk}`);
});
res.on('end', () => {
console.log('Response complete');
});
});
req.on('error', (err) => {
console.error(`Error: ${err.message}`);
});
GET requests include query parameters in the URL and have no request body. When you need to send data — a JSON payload, form fields, or file uploads — switch to https.request() with the POST method. You write the body to the request object with req.write() and must call req.end() to signal the request is complete. The Content-Length header tells the server how much data to expect; for dynamic payloads, omit it and Node.js will use chunked transfer encoding.
Making HTTPS POST requests with options
const https = require('https');
const data = JSON.stringify({ name: 'John', age: 30 });
const options = {
hostname: 'api.example.com',
port: 443,
path: '/users',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
}
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
console.log('Response:', body);
});
});
req.write(data);
req.end();
Key Options
When creating an HTTPS server or making HTTPS requests, you can configure TLS/SSL options:
| Option | Type | Description |
|---|---|---|
key | string | Buffer | Private key in PEM format |
cert | string | Buffer | Certificate in PEM format |
ca | string | Buffer | Certificate Authority certificate(s) |
rejectUnauthorized | boolean | Whether to reject unauthorized certificates |
pfx | string | Buffer | Private key, certificate, and CA certs in PFX/PKCS12 format |
Server-Side Examples
Using a self-signed certificate (development)
const https = require('https');
const fs = require('fs');
const crypto = require('crypto');
function generateSelfSigned() {
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
return { privateKey, publicKey };
}
const { privateKey, publicKey } = generateSelfSigned();
const server = https.createServer({
key: privateKey,
cert: publicKey
}, (req, res) => {
res.writeHead(200);
res.end('Secure (but self-signed)!');
});
server.listen(443);
Self-signed certificates are enough for localhost development — no CA is involved. But in environments where the client itself must prove its identity, mutual TLS (mTLS) requires the server to demand and verify a client certificate. This adds a second layer of trust: the client authenticates the server, and the server authenticates the client.
Requiring client certificates (mutual TLS)
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert'),
ca: fs.readFileSync('client-ca.crt'), // CA that signed client certs
requestCert: true, // Require client certificate
rejectUnauthorized: true // Reject unauthorized clients
};
const server = https.createServer(options, (req, res) => {
// Client certificate available in req.socket.getPeerCertificate()
console.log('Client:', req.socket.getPeerCertificate().subject);
res.writeHead(200);
res.end('Authenticated!');
});
server.listen(443);
The server-side examples cover the server’s TLS configuration. On the client side, you face a different problem: how to connect to a server whose certificate is not trusted by the default CA store. The next two examples show the wrong way and the right way to handle this.
Client-Side Examples
Disabling certificate verification (development only!)
const https = require('https');
const options = {
hostname: 'self-signed.example.com',
port: 443,
rejectUnauthorized: false // ⚠️ Never use in production!
};
const req = https.get(options, (res) => {
console.log('STATUS:', res.statusCode);
res.on('data', console.log);
});
req.on('error', console.error);
Setting rejectUnauthorized: false is a security hole — it accepts any certificate, including one presented by an attacker. The safe alternative is to provide the specific CA certificate that signed the server’s certificate via the ca option. Node.js will then validate the server’s certificate against only that CA, keeping the connection authenticated without weakening TLS.
Using Custom CA
const https = require('https');
const fs = require('fs');
const options = {
hostname: 'api.internal.company.com',
port: 443,
ca: fs.readFileSync('company-ca.crt') // Custom CA certificate
};
const req = https.get(options, (res) => {
console.log('STATUS:', res.statusCode);
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => { console.log(data); });
});
req.on('error', console.error);
Key Differences from HTTP
- Default Port: HTTP uses port 80, HTTPS uses port 443
- Encryption: All data is encrypted using TLS/SSL
- Certificate Required: Servers need SSL certificates
- Module API: Nearly identical to
httpmodule, but with TLS options
https vs http module
The node:https module is nearly identical to node:http in its API — https.createServer(), https.request(), and https.get() mirror their http counterparts exactly. The key difference is that HTTPS requires a TLS certificate and private key, which you pass as key and cert options when creating a server. On the client side, HTTPS validates the server’s certificate by default. If the certificate is self-signed or from an unknown CA, https.get() will reject the connection unless you set rejectUnauthorized: false (for development only) or provide the CA certificate.
Certificate validation and security
By default, Node.js validates the server’s SSL certificate using the built-in Mozilla trust store. Setting rejectUnauthorized: false disables this check entirely, which exposes connections to man-in-the-middle attacks. The correct approach for internal APIs with self-signed certificates is to pass the CA certificate via the ca option rather than disabling validation. For production servers, use certificates from a trusted CA such as Let’s Encrypt, which provides free certificates via the ACME protocol.
When to use https vs fetch
In modern Node.js (18+), the global fetch() function is available and handles both HTTP and HTTPS transparently. The node:https module is lower-level and gives you more control over connection behavior — streaming large responses, custom TLS settings, keep-alive connections, and mutual TLS. For simple API calls, fetch() is cleaner. For high-performance servers, custom TLS configurations, or reading binary streams, the https module is more appropriate.
See Also
- node:http — HTTP server and client
- node:url — URL parsing and manipulation
- node:crypto — Cryptographic functions