bugl
bugl
HomeLearnPatternsPathsSearch
HomeLearnPatternsPathsSearch

Loading lesson path

Learn/Node.js/Node.js Advanced
Node.js•Node.js Advanced

Node.js TLS/SSL Module

Flash cards

Review the key moves

1/4
Core idea

What is the main idea behind Node.js TLS/SSL Module?

Lesson checks

Practice each idea before moving on

Short Mimo-style checks built from this lesson's code, terms, and sequence.

1Quick choice

Which statement best captures the main point of this lesson?

2Fill blank

Complete the missing token from the example code.

___ tls = require('tls');
3Order

Put the learning moves in the order that makes the concept easiest to apply.

Server and Client Options
Generating Self-Signed Certificates for Development
Using the TLS Module

What is TLS/SSL?

Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL), are protocols that provide secure communication over a computer network. They ensure:

  • Privacy : Communications are encrypted to prevent eavesdropping
  • Data integrity : Message contents cannot be modified without detection
  • Authentication : The identities of the communicating parties can be verified

TLS/SSL is commonly used for securing

  • Web browsing (HTTPS)
  • Email transmissions (SMTP, IMAP, POP3)
  • Instant messaging
  • Voice over IP (VoIP)
  • API communication

Using the TLS Module

To use the TLS module in Node.js, you need to require it:

const tls = require('tls');

TLS Server

Here's how to create a basic TLS server:

const tls = require('tls');
const fs = require('fs');
const path = require('path');
// Server options with TLS certificates
const options = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
 requestCert: true,
 // Reject connections without authorized certificates (optional)
 rejectUnauthorized: false
};
// Create TLS server
const server = tls.createServer(options, (socket) => {
 console.log('Server connected',
 socket.authorized ? 'authorized' : 'unauthorized');
 // Set encoding for data
 socket.setEncoding('utf8');
 // Handle incoming data
 socket.on('data', (data) => {
 console.log('Received:', data);
 // Echo back the data
 socket.write(`You said: ${data}`);
 });
 // Handle socket closure
 socket.on('end', () => {
 console.log('Socket ended');
 });
 // Write welcome message
 socket.write('Welcome to the TLS server!\n');
});
// Start TLS server
const port = 8000;
server.listen(port, () => {
 console.log(`TLS server running on port ${port}`);
});

Generating Self-Signed Certificates for Development

You can use OpenSSL to generate self-signed certificates for development and testing: openssl genrsa -out ca-key.pem 2048 openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 365 openssl genrsa -out server-key.pem 2048 openssl req -new -key server-key.pem -out server-csr.pem openssl x509 -req -in server-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365 openssl genrsa -out client-key.pem 2048 openssl req -new -key client-key.pem -out client-csr.pem openssl x509 -req -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365

TLS Client

Creating a client that connects to a TLS server:

const tls = require('tls');
const fs = require('fs');
const path = require('path');
// Client options
const options = {
 // For mutual authentication (optional)
 key: fs.readFileSync(path.join(__dirname, 'client-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'client-cert.pem')),
 // Server name for Server Name Indication (SNI)
 servername: 'localhost',
 ca: fs.readFileSync(path.join(__dirname, 'ca-cert.pem')),
 // Reject unauthorized certificates
 rejectUnauthorized: true
};
// Connect to server
const client = tls.connect(8000, 'localhost', options, () => {
 // Check if authorized
 console.log('Client connected',
 client.authorized ? 'authorized' : 'unauthorized');
 if (!client.authorized) {
 console.log('Reason:', client.authorizationError);
 }
 // Send data to server
 client.write('Hello from TLS client!');
});
// Set encoding for received data
client.setEncoding('utf8');
// Handle received data
client.on('data', (data) => {
 console.log('Received from server:', data);
 // Send another message
 client.write('How are you?');
});
// Handle errors
client.on('error', (error) => {
 console.error('Connection error:', error);
});
// Handle connection end
client.on('end', () => {
 console.log('Server ended connection');
});
// Close connection after 5 seconds
setTimeout(() => {
 console.log('Closing connection');
 client.end();
}, 5000);

Server and Client Options

Both tls.createServer() and tls.connect() accept various options to configure the TLS connection:

Common Options

  • key : Private key in PEM format
  • ca : Trusted CA certificates
  • ciphers : Cipher suite specification string
  • minVersion : Minimum TLS version to allow
  • maxVersion : Maximum TLS version to allow

Server-specific Options

  • rejectUnauthorized : Whether to reject clients with invalid certificates
  • SNICallback : Function to handle SNI from the client

Client-specific Options

  • servername : Server name for SNI
  • checkServerIdentity : Function to verify server hostname
  • session : A Buffer instance containing TLS session
const tls = require('tls');
const fs = require('fs');
// Comprehensive server options
const serverOptions = {
 key: fs.readFileSync('server-key.pem'),
 cert: fs.readFileSync('server-cert.pem'),
 ca: [fs.readFileSync('ca-cert.pem')],
 // Protocol version control
 minVersion: 'TLSv1.2',
 maxVersion: 'TLSv1.3',
 // Cipher control
 ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384',
 // Client authentication
 requestCert: true,
 rejectUnauthorized: true,
 // Server Name Indication handling
 SNICallback: (servername, cb) => {
 // Different certificates for different servernames
 if (servername === 'example.com') {
 cb(null, tls.createSecureContext({
 key: fs.readFileSync('example-key.pem'),
 cert: fs.readFileSync('example-cert.pem')
 }));
 } else {
 cb(null, tls.createSecureContext({
 key: fs.readFileSync('default-key.pem'),
 cert: fs.readFileSync('default-cert.pem')
 }));
 }
}
};
// Example client options
const clientOptions = {
 key: fs.readFileSync('client-key.pem'),
 cert: fs.readFileSync('client-cert.pem'),
 ca: [fs.readFileSync('ca-cert.pem')],
 servername: 'example.com',
 minVersion: 'TLSv1.2',
 // Custom identity check function
 checkServerIdentity: (hostname, cert) => {
 // Custom validation logic
 if (hostname !== cert.subject.CN) {
 }
 return undefined; // No error
 },
 // Session reuse
 session: savedTlsSession, // Previously saved session
};

Secure HTTP Server (HTTPS)

While the TLS module can be used directly, for HTTPS servers, Node.js provides a higher-level https module built on top of TLS:

const https = require('https');
const fs = require('fs');
const path = require('path');
// HTTPS server options
const options = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem'))
};
// Create HTTPS server
https.createServer(options, (req, res) => {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.end('<h1>Secure HTTPS Server</h1><p>This connection is encrypted using TLS.</p>');
}).listen(443, () => {
console.log('HTTPS server running on port 443');
});

The HTTPS module provides a more convenient way to create secure HTTP servers, but it uses the TLS module under the hood.

TLS with Express

You can also create an HTTPS server with Express:

const express = require('express');
const https = require('https');
const fs = require('fs');
const path = require('path');
// Create Express app
const app = express();
// Define routes
app.get('/', (req, res) => {
 res.send('<h1>Secure Express App</h1><p>This connection is encrypted using TLS.</p>');
});
app.get('/api/data', (req, res) => {
 res.json({
 message: 'This is sensitive data',
 timestamp: new Date()
 });
});
// HTTPS server options
const options = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem'))
};
// Create HTTPS server with Express app
const port = 443;
https.createServer(options, app).listen(port, () => {
 console.log(`Secure Express app running on port ${port}`);
});
const tls = require('tls');
const fs = require('fs');
// Custom verification function
function validateCertificate(cert) {
 console.log('Valid from:', cert.valid_from);
 console.log('Valid to:', cert.valid_to);
 const now = new Date();
 const validFrom = new Date(cert.valid_from);
 const validTo = new Date(cert.valid_to);
 if (now < validFrom || now > validTo) {
 }
 // Additional checks could include:
 // - Public key strength
 return { valid: true };
}
// Create TLS client with custom validation
const options = {
 ca: [fs.readFileSync('ca-cert.pem')],
 checkServerIdentity: (hostname, cert) => {
 const validationResult = validateCertificate(cert);
 if (!validationResult.valid) {
 return new Error(validationResult.reason);
 }
 const certCN = cert.subject.CN;
 if (hostname !== certCN &&
 !cert.subjectaltname ||
 !cert.subjectaltname.includes(hostname)) {
 }
 return undefined;
 }
};
// Connect to server with custom verification
const client = tls.connect(8000, 'example.com', options, () => {
 if (client.authorized) {
 console.log('Connection authorized');
 client.write('Secure message');
 } else {
 console.log('Connection not authorized:', client.authorizationError);
}
});
// Handle connection events
client.on('error', (error) => {
 console.error('TLS error:', error);
});
client.on('end', () => {
 console.log('Connection ended');
});

TLS Session Resumption

Session resumption allows clients to reconnect to a server without performing a full TLS handshake, improving performance:

const tls = require('tls');
const fs = require('fs');
const path = require('path');
// Server options
const serverOptions = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
 // Enable session resumption
 sessionTimeout: 300, // Session timeout in seconds
 ticketKeys: Buffer.from('0123456789abcdef0123456789abcdef'), // 32 bytes for key encryption
};
// Create TLS server
const server = tls.createServer(serverOptions, (socket) => {
 console.log('Client connected');
 // Check if this is a resumed session
 if (socket.isSessionReused()) {
 console.log('Session reused!');
 } else {
 console.log('New session');
}
socket.on('data', (data) => {
 console.log('Received:', data.toString());
 socket.write('Hello back!');
});
socket.on('end', () => {
 console.log('Client disconnected');
});
});
server.listen(8443, () => {
 console.log('TLS server listening on port 8443');
 // First client connection
 connectClient(() => {
 // Second client connection - should use session resumption
 connectClient();
 });
});
// Function to create a client with session resumption
let savedSession = null;
function connectClient(callback) {
 const clientOptions = {
 rejectUnauthorized: false, // For self-signed certificates
 session: savedSession // Use saved session if available
 };
 const client = tls.connect(8443, 'localhost', clientOptions, () => {
 console.log('Client connected. Authorized:', client.authorized);
 console.log('Using session resumption:', client.isSessionReused());
 // Save the session for future connections
 savedSession = client.getSession();
 // Send data
 client.write('Hello server!');
 // Close after a short delay
 setTimeout(() => {
 client.end();
 if (callback) setTimeout(callback, 100);
 }, 100);
 });
 client.on('data', (data) => {
 console.log('Client received:', data.toString());
 });
 client.on('error', (err) => {
 console.error('Client error:', err);
 });
}

Server Name Indication (SNI)

SNI allows a server to present different certificates for different hostnames on the same IP address and port:

const tls = require('tls');
const fs = require('fs');
const path = require('path');
// Load different certificates for different domains
const serverOptions = {
 SNICallback: (servername, cb) => {
 console.log(`SNI request for: ${servername}`);
 if (servername === 'example.com') {
 const context = tls.createSecureContext({
 key: fs.readFileSync(path.join(__dirname, 'example.com-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'example.com-cert.pem'))
 });
 cb(null, context);
 }
 else if (servername === 'another.com') {
 const context = tls.createSecureContext({
 key: fs.readFileSync(path.join(__dirname, 'another.com-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'another.com-cert.pem'))
 });
 cb(null, context);
 }
 else {
 const context = tls.createSecureContext({
 key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
 });
 cb(null, context);
 }
 },
 // Default keys and certificates (used as a fallback)
 key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
};
// Create server
const server = tls.createServer(serverOptions, (socket) => {
 socket.write(`Hello, you connected to ${socket.servername || 'unknown'}!\n`);
 socket.end();
});
server.listen(8443, () => {
 console.log('TLS SNI server running on port 8443');
});
const tls = require('tls');
const fs = require('fs');
const path = require('path');
// Load multiple CA certificates
const caCerts = [
 fs.readFileSync(path.join(__dirname, 'ca1-cert.pem')),
 fs.readFileSync(path.join(__dirname, 'ca2-cert.pem')),
 fs.readFileSync(path.join(__dirname, 'intermediate-cert.pem'))
];
// Server with multiple CA certificates
const serverOptions = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
 ca: caCerts, // Array of CA certificates
 requestCert: true,
 rejectUnauthorized: true
};
const server = tls.createServer(serverOptions, (socket) => {
 console.log('Client connected:', socket.authorized ? 'Authorized' : 'Unauthorized');
 const cert = socket.getPeerCertificate();
 console.log('Issuer:', cert.issuer.CN);
 socket.write('Welcome to the secure server!\n');
 socket.end();
});
server.listen(8000, () => {
 console.log('TLS server running on port 8000');
});
const tls = require('tls');
const fs = require('fs');
const crypto = require('crypto');
const crl = fs.readFileSync('revoked-certs.pem');
// Parse CRL to check against
const checkRevocation = (cert) => {
 // In a real application, you would parse the CRL and check
 // For demonstration, we'll just check against a known revoked serial
 const revokedSerials = [
 '0123456789ABCDEF', // Example revoked serial
 'FEDCBA9876543210'
 ];
 const certInfo = crypto.certificateVerify(
 cert.raw,
 'sha256',
 Buffer.from(''),
 Buffer.from('')
);
return !revokedSerials.includes(certInfo.serialNumber.toString('hex').toUpperCase());
};
const server = tls.createServer({
 key: fs.readFileSync('server-key.pem'),
 cert: fs.readFileSync('server-cert.pem'),
 requestCert: true,
 rejectUnauthorized: true,
 checkServerIdentity: (host, cert) => {
 if (!checkRevocation(cert)) {
 }
 }
}, (socket) => {
// Handle connection
console.log('Client connected:', socket.authorized ? 'Authorized' : 'Unauthorized');
socket.end('Hello secure world!\n');
});
server.listen(8000);
const tls = require('tls');
const https = require('https');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
class TLSCertManager {
 constructor(domain, email) {
 this.domain = domain;
 this.email = email;
 this.certDir = path.join(__dirname, 'certs', domain);
 this.ensureCertDir();
 }
 ensureCertDir() {
 if (!fs.existsSync(this.certDir)) {
 fs.mkdirSync(this.certDir, { recursive: true });
 }
 }
 async getCertificates() {
 const keyPath = path.join(this.certDir, 'privkey.pem');
 const certPath = path.join(this.certDir, 'cert.pem');
 const chainPath = path.join(this.certDir, 'chain.pem');
 // Check if certificates exist and are valid
 if (this.certsValid(keyPath, certPath, chainPath)) {
 return {
 key: fs.readFileSync(keyPath),
 cert: fs.readFileSync(certPath),
 ca: fs.readFileSync(chainPath)
 };
 }
 // Use certbot to obtain new certificates
 return await this.obtainCertificates();
 }
 certsValid(keyPath, certPath, chainPath) {
 try {
 if (!fs.existsSync(keyPath) || !fs.existsSync(certPath) || !fs.existsSync(chainPath)) {
 return false;
 }
 const cert = fs.readFileSync(certPath);
 const notAfter = cert.toString().match(/Not After : (.*?)\n/)[1];
 const expiryDate = new Date(notAfter);
 const now = new Date();
 return (expiryDate - now) > 7 * 24 * 60 * 60 * 1000; // 7 days in ms
 } catch (err) {
 return false;
 }
}
async obtainCertificates() {
 try {
 // This is a simplified example - in production, use a proper ACME client
 console.log('Obtaining new certificates from Let\'s Encrypt...');
 // In a real application, you would use an ACME client like 'greenlock' or 'acme'
 // This is just a placeholder to illustrate the concept
 execSync(`certbot certonly --standalone -d ${this.domain} --email ${this.email} --agree-tos --non-interactive`);
 // Copy certificates to our certs directory
 const certs = {
 key: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/privkey.pem`),
 cert: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/cert.pem`),
 ca: fs.readFileSync(`/etc/letsencrypt/live/${this.domain}/chain.pem`)
 };
 // Save certificates for future use
 fs.writeFileSync(path.join(this.certDir, 'privkey.pem'), certs.key);
 fs.writeFileSync(path.join(this.certDir, 'cert.pem'), certs.cert);
 fs.writeFileSync(path.join(this.certDir, 'chain.pem'), certs.ca);
 return certs;
 } catch (err) {
 console.error('Failed to obtain certificates:', err);
 throw err;
}
}
}
// Usage example
async function createSecureServer() {
 const certManager = new TLSCertManager('example.com', 'admin@example.com');
 try {
 const certs = await certManager.getCertificates();
 const server = https.createServer({
 key: certs.key,
 cert: certs.cert,
 ca: certs.ca,
 requestCert: true,
 rejectUnauthorized: true
 }, (req, res) => {
 res.writeHead(200);
 res.end('Hello, secure world!\n');
 });
 server.listen(443, () => {
 console.log('HTTPS server running on port 443');
 });
 setInterval(async () => {
 try {
 await certManager.getCertificates();
 } catch (err) {
 }
}, 24 * 60 * 60 * 1000); // Check daily
} catch (err) {
console.error('Failed to start secure server:', err);
process.exit(1);
}
}
createSecureServer();

Note

The Let's Encrypt example is simplified. In production, use a well-maintained ACME client library and follow Let's Encrypt's rate limits and best practices.

Security Best Practices

When using TLS in production applications, consider these security best practices:

Use Strong TLS Versions

const options = {
 // Disable older TLS versions
 minVersion: 'TLSv1.2',
 // Explicitly disallow TLS 1.0 and 1.1
 secureOptions: crypto.constants.SSL_OP_NO_TLSv1 |
 crypto.constants.SSL_OP_NO_TLSv1_1
};

Configure Strong Cipher Suites

const options = {
 // Prioritize modern, secure cipher suites
 ciphers: [
 'TLS_AES_256_GCM_SHA384',
 'TLS_CHACHA20_POLY1305_SHA256',
 'TLS_AES_128_GCM_SHA256',
 'ECDHE-RSA-AES256-GCM-SHA384',
 'ECDHE-RSA-AES128-GCM-SHA256'
 ].join(':')
};

Use Perfect Forward Secrecy

// Cipher suites with ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) support PFS
const options = {
 ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
};
const tls = require('tls');
const https = require('https');
const fs = require('fs');
const path = require('path');
// Server with OCSP stapling
const serverOptions = {
 key: fs.readFileSync(path.join(__dirname, 'server-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'server-cert.pem')),
 ca: fs.readFileSync(path.join(__dirname, 'ca-cert.pem')),
 // Enable OCSP stapling
 requestOCSP: true,
 // OCSP response cache timeout (in milliseconds)
 ocspCache: new tls.OCSPCache({
 max: 1000, // Maximum number of cached responses
 maxAge: 60 * 60 * 1000 // Cache for 1 hour
 })
};
// Create HTTPS server with OCSP stapling
const server = https.createServer(serverOptions, (req, res) => {
 res.writeHead(200);
 res.end('Hello with OCSP stapling!\n');
});
// Handle OCSP request errors
server.on('OCSPRequest', (cert, issuer, callback) => {
 if (!cert || !issuer) {
 }
 const ocspUrl = tls.getOCSPURL(cert);
 if (!ocspUrl) {
 }
 console.log('OCSP request for:', cert.subject.CN);
 // In a real application, you would make an OCSP request here
 // and return the response via the callback
 // For demonstration, we'll just return a dummy response
 const ocspResponse = Buffer.from('OCSP response would go here');
 callback(null, ocspResponse);
});
server.listen(443, () => {
 console.log('HTTPS server with OCSP stapling running on port 443');
});
// Client that verifies OCSP stapling
const clientOptions = {
 host: 'example.com',
 port: 443,
 rejectUnauthorized: true,
 requestOCSP: true // Request OCSP stapling from server
};
const req = https.request(clientOptions, (res) => {
 console.log('Response status code:', res.statusCode);
 // Get the OCSP response from the server
 const ocspResponse = res.socket.getOCSPResponse();
 if (ocspResponse) {
 console.log('Received OCSP response');
 // Verify the OCSP response here
 } else {
 console.log('No OCSP response received');
}
res.on('data', (chunk) => {
 console.log('Received data:', chunk.toString());
});
});
req.on('error', (err) => {
 console.error('Request error:', err);
});
req.end();

ALPN and SNI Support

Application-Layer Protocol Negotiation (ALPN) and Server Name Indication (SNI) are important TLS extensions that enable protocol negotiation and virtual hosting:

const tls = require('tls');
const http2 = require('http2');
const https = require('https');
const fs = require('fs');
const path = require('path');
// Server with ALPN and SNI support
const serverOptions = {
 // ALPN protocols in order of preference
 ALPNProtocols: ['h2', 'http/1.1'],
 // SNI callback for multiple domains
 SNICallback: (servername, cb) => {
 console.log('SNI request for:', servername);
 try {
 let context;
 // Create different contexts for different domains
 if (servername === 'example.com') {
 context = tls.createSecureContext({
 key: fs.readFileSync(path.join(__dirname, 'example.com-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'example.com-cert.pem')),
 // Enable OCSP stapling for this domain
 requestOCSP: true,
 // Custom cipher suites for this domain
 ciphers: [
 'TLS_AES_256_GCM_SHA384',
 'TLS_CHACHA20_POLY1305_SHA256',
 'TLS_AES_128_GCM_SHA256'
 ].join(':')
 });
 } else {
 // Default context for other domains
 context = tls.createSecureContext({
 key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
 cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem')),
 // Less strict ciphers for legacy clients
 ciphers: [
 'TLS_AES_256_GCM_SHA384',
 'TLS_CHACHA20_POLY1305_SHA256',
 'TLS_AES_128_GCM_SHA256',
 'ECDHE-RSA-AES256-GCM-SHA384',
 'ECDHE-RSA-AES128-GCM-SHA256'
 ].join(':')
 });
 }
 // Set ALPN protocols for this context
 context.setALPNProtocols(['h2', 'http/1.1']);
 // Return the created context
 if (cb) {
 cb(null, context);
 } else {
 return context;
 }
} catch (err) {
console.error('SNI callback error:', err);
if (cb) {
 cb(err);
} else {
throw err;
}
}
},
// Default key and cert (used if SNI is not supported by client)
key: fs.readFileSync(path.join(__dirname, 'default-key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'default-cert.pem'))
};
// Create HTTP/2 server with ALPN and SNI
const http2Server = http2.createSecureServer(serverOptions, (req, res) => {
 const protocol = req.socket.alpnProtocol;
 res.writeHead(200);
 res.end(`Hello from ${req.socket.servername} using ${protocol}\n`);
});
http2Server.on('error', (err) => {
 console.error('HTTP/2 server error:', err);
});
http2Server.on('stream', (stream, headers) => {
 const protocol = stream.session.alpnProtocol;
 const hostname = stream.session.servername || 'unknown';
 stream.respond({
 'content-type': 'text/plain; charset=utf-8',
 ':status': 200
 });
 stream.end(`HTTP/2 stream from ${hostname} using ${protocol}\n`);
});
// Create HTTPS server with ALPN and SNI
const httpsServer = https.createServer(serverOptions, (req, res) => {
 const protocol = req.socket.alpnProtocol;
 res.writeHead(200, { 'Content-Type': 'text/plain' });
 res.end(`Hello from ${req.socket.servername} using ${protocol || 'HTTP/1.1'}\n`);
});
// Handle upgrade to HTTP/2
httpsServer.on('upgrade', (req, socket, head) => {
 const protocol = req.socket.alpnProtocol;
 if (protocol === 'h2') {
 http2Server.emit('connection', socket);
 } else {
 socket.destroy();
}
});
// Start servers
const PORT = 443;
httpsServer.listen(PORT, () => {
 console.log(`HTTPS server running on port ${PORT}`);
});
// Client example
function makeRequest(hostname, port = 443) {
 const options = {
 hostname,
 port,
 path: '/',
 method: 'GET',
 // Enable ALPN
 ALPNProtocols: ['h2', 'http/1.1'],
 // Set SNI
 servername: hostname,
 rejectUnauthorized: false, // For testing with self-signed certs
 // Custom check for server identity
 checkServerIdentity: (host, cert) => {
 return undefined; // No error means success
 }
 };
 const req = https.request(options, (res) => {
 console.log(`Status: ${res.statusCode}`);
 console.log('ALPN Protocol:', res.socket.alpnProtocol);
 console.log('Negotiated Protocol:', res.socket.getProtocol());
 let data = '';
 res.on('data', (chunk) => {
 data += chunk;
 });
 res.on('end', () => {
 console.log('Response:', data.trim());
 });
 });
 req.on('error', (err) => {
 console.error('Request error:', err);
 });
 req.end();
}
// Example usage
// makeRequest('example.com');
// makeRequest('another-domain.com');

Use HTTP Strict Transport Security (HSTS)

// In an Express application
app.use((req, res, next) => {
 res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
 next();
});

Previous

Node.js VM Module

Next

Node.js Net Module