This error occurs when a Node.js client attempts to connect to a server using incompatible TLS/SSL protocol versions. The TLS handshake fails because the client and server cannot agree on a mutually supported protocol version.
The UNSUPPORTED_PROTOCOL error indicates that the TLS (Transport Layer Security) version negotiation between your Node.js application and a remote server has failed. This happens during the SSL/TLS handshake when the client and server try to agree on a protocol version to use for secure communication. Node.js 12 and later versions default to TLS 1.2 as the minimum supported protocol version for security reasons. When your application tries to connect to a server that only supports older protocols (like TLS 1.0 or 1.1), or when a server requires a newer protocol version that your Node.js version doesn't support, the connection fails with this error. The error message often appears as "ssl_choose_client_version:unsupported protocol" in the underlying OpenSSL error stack, indicating that the SSL library couldn't select a compatible protocol version during the client-server negotiation.
First, identify which TLS versions your Node.js installation supports and which the server requires.
Check Node.js TLS support:
node -p "process.versions.openssl"
node -p "require('tls').DEFAULT_MIN_VERSION"
node -p "require('tls').DEFAULT_MAX_VERSION"Test the server's TLS support using OpenSSL:
# Test TLS 1.0
openssl s_client -connect example.com:443 -tls1
# Test TLS 1.1
openssl s_client -connect example.com:443 -tls1_1
# Test TLS 1.2
openssl s_client -connect example.com:443 -tls1_2
# Test TLS 1.3
openssl s_client -connect example.com:443 -tls1_3This helps you understand which protocol versions are available and where the incompatibility lies.
The recommended approach is to explicitly set the TLS version range in your HTTPS requests using minVersion and maxVersion options.
For HTTPS requests:
const https = require('https');
const options = {
hostname: 'example.com',
port: 443,
path: '/',
method: 'GET',
// Set minimum TLS version (e.g., to allow TLS 1.0 for legacy servers)
minVersion: 'TLSv1',
// Or set a specific secure protocol
// secureProtocol: 'TLSv1_2_method'
};
https.request(options, (res) => {
console.log('Connection successful');
}).end();For HTTPS agents (reusable connections):
const https = require('https');
const agent = new https.Agent({
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3'
});
https.get('https://example.com', { agent }, (res) => {
console.log('Connected with TLS version:', res.socket.getProtocol());
});For Axios or other libraries:
const axios = require('axios');
const https = require('https');
const instance = axios.create({
httpsAgent: new https.Agent({
minVersion: 'TLSv1.2'
})
});
instance.get('https://example.com')
.then(response => console.log('Success'))
.catch(error => console.error('Error:', error.message));If you need to change TLS behavior for your entire application without modifying code, use Node.js command-line flags.
Allow TLS 1.0 (for legacy server compatibility):
node --tls-min-v1.0 app.jsEnforce TLS 1.2 minimum:
node --tls-min-v1.2 app.jsSet maximum TLS version:
node --tls-max-v1.2 app.jsCombine with other flags:
node --tls-min-v1.0 --tls-max-v1.2 app.jsYou can also set these in your package.json scripts:
{
"scripts": {
"start": "node --tls-min-v1.0 app.js"
}
}Note: The --tls-v1.0 flag format used in older Node.js versions has been deprecated in favor of --tls-min-v1.0.
Rather than downgrading client security, the best solution is to upgrade the server to support modern TLS versions.
For servers you control, enable TLS 1.2 and 1.3:
Nginx:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;Apache:
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5Node.js HTTPS server:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3'
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Secure connection\n');
}).listen(443);After updating the server configuration, restart the service and test the connection from your Node.js client without special TLS flags.
After implementing your fix, verify that the TLS connection succeeds and uses the expected protocol version.
Test your connection and log the TLS version:
const https = require('https');
const options = {
hostname: 'example.com',
port: 443,
path: '/',
method: 'GET'
};
const req = https.request(options, (res) => {
console.log('Connection successful!');
console.log('TLS Version:', res.socket.getProtocol());
console.log('Cipher:', res.socket.getCipher());
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', (e) => {
console.error('Connection failed:', e.message);
});
req.end();Expected output for a successful TLS 1.2 connection:
Connection successful!
TLS Version: TLSv1.2
Cipher: { name: 'ECDHE-RSA-AES128-GCM-SHA256', version: 'TLSv1.2' }Security Considerations:
Downgrading to TLS 1.0 or 1.1 should only be a temporary solution for legacy system compatibility. These protocols have known vulnerabilities and are deprecated. PCI DSS compliance requires TLS 1.2 or higher for payment processing systems.
OpenSSL Version Impact:
Node.js TLS support depends on the underlying OpenSSL version. Older Node.js versions compiled against older OpenSSL may not support TLS 1.3. Check your OpenSSL version with node -p "process.versions.openssl".
AWS SDK Specific Configuration:
If you're using AWS SDK for JavaScript, you can enforce TLS 1.2 globally:
const AWS = require('aws-sdk');
const https = require('https');
AWS.config.update({
httpOptions: {
agent: new https.Agent({
minVersion: 'TLSv1.2'
})
}
});Cipher Suite Compatibility:
Sometimes the protocol version matches but cipher suites don't. If you continue having issues after setting minVersion, try specifying compatible ciphers:
const agent = new https.Agent({
minVersion: 'TLSv1.2',
ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
});Docker and Container Environments:
Base images with outdated OpenSSL versions can cause TLS issues. Use recent base images like node:18-alpine or node:20 that include modern OpenSSL versions with full TLS 1.3 support.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process