SSL protocol errors occur when Node.js cannot verify an HTTPS server's certificate. This happens due to missing intermediate certificates, self-signed certificates, expired certificates, or incorrect system time. Learn how to diagnose and fix certificate verification failures safely.
This error occurs when your Node.js application attempts to make an HTTPS request (or establish a TLS connection) but cannot verify the server's SSL/TLS certificate. Node.js validates certificates by checking the complete certificate chain: the server certificate, any intermediate certificates, and the root certificate. If any part of this chain is missing, improperly configured, or issued by an untrusted Certificate Authority, Node.js rejects the connection. The error does not mean the certificate is necessarily invalid—it means your system cannot establish trust in the certificate through the standard verification process. This is a security feature that prevents man-in-the-middle (MITM) attacks and other certificate-based threats.
SSL certificates have validity periods (not-before and not-after dates). If your system clock is significantly wrong, valid certificates may appear expired or not-yet-valid.
Check current time:
dateIf the time is incorrect, sync your system clock using NTP (Network Time Protocol):
Linux/macOS:
sudo ntpdate -s time.nist.gov
# or
sudo timedatectl set-ntp trueWindows:
w32tm /resyncRestart your Node.js application after correcting the time.
Verify the expiration date of the server's certificate using OpenSSL:
openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep -A 5 "notAfter"Or for a specific port:
echo | openssl s_client -connect api.example.com:8443 2>/dev/null | openssl x509 -noout -datesIf the certificate has expired, the server administrator must renew it. If it's a self-signed certificate you control, generate a new one with an extended validity period:
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365Get the full certificate chain from the server:
openssl s_client -connect example.com:443 -showcerts 2>/dev/null > fullchain.pemThis will output all certificates in the chain. Save this file for the next steps.
Alternatively, download just the certificate using a web browser (click the padlock icon) or use:
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -outform PEM > server-cert.pemFor self-signed or internal CA certificates, tell Node.js to trust them by setting the NODE_EXTRA_CA_CERTS environment variable:
Linux/macOS:
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
node your-app.jsWindows (PowerShell):
$env:NODE_EXTRA_CA_CERTS = "C:\path\to\ca-cert.pem"
node your-app.jsWindows (Command Prompt):
set NODE_EXTRA_CA_CERTS=C:\path\to\ca-cert.pem
node your-app.jsIf you have multiple CA certificates, concatenate them into a single file:
cat ca1.pem ca2.pem ca3.pem > ca-bundle.pem
export NODE_EXTRA_CA_CERTS=/path/to/ca-bundle.pem
node your-app.jsThis is the recommended approach for corporate or development environments with internal CAs.
For more control, you can also configure certificates directly in your Node.js code:
import https from 'https';
import fs from 'fs';
const ca = fs.readFileSync('/path/to/ca-cert.pem', 'utf8');
const options = {
hostname: 'api.example.com',
port: 443,
path: '/endpoint',
method: 'GET',
ca: ca, // Add the CA certificate
};
const req = https.request(options, (res) => {
res.on('data', (chunk) => {
console.log('Received:', chunk.toString());
});
});
req.on('error', (err) => {
console.error('Request failed:', err.message);
});
req.end();For fetch (Node.js 18+):
import https from 'https';
import fs from 'fs';
const ca = fs.readFileSync('/path/to/ca-cert.pem', 'utf8');
const agent = new https.Agent({ ca });
const response = await fetch('https://api.example.com/endpoint', {
agent,
});For axios:
import https from 'https';
import fs from 'fs';
import axios from 'axios';
const ca = fs.readFileSync('/path/to/ca-cert.pem', 'utf8');
const httpsAgent = new https.Agent({ ca });
axios.get('https://api.example.com/endpoint', {
httpsAgent,
});Outdated Node.js versions may have incomplete or outdated CA certificate bundles. Update to the latest stable release:
# Check current version
node --version
# Update Node.js (use nvm, brew, or download from nodejs.org)
nvm install node # if using nvm
# or
brew upgrade node # if using Homebrew
# Update npm packages
npm update
npm audit fixAfter updating, restart your application.
For development environments, you can permanently add a self-signed certificate to your system's trusted CA store:
Linux (Debian/Ubuntu):
sudo cp ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt
sudo update-ca-certificatesmacOS:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca-cert.pemWindows:
Right-click the .pem or .crt file → Install Certificate → Select "Local Machine" → Place in "Trusted Root Certification Authorities"
After adding, restart Node.js to apply the changes.
Corporate Proxies and Antivirus Software:
Some corporate environments use SSL inspection (decrypting HTTPS traffic to scan it). This replaces the server's certificate with a proxy certificate, causing verification failures. If your IT department has deployed such tools, they should provide a root CA certificate to add to NODE_EXTRA_CA_CERTS.
Intermediate Certificate Chain:
The complete certificate chain must be in the correct order: server certificate → intermediate CA(s) → root CA. If your server is only sending the server certificate without intermediates, the verification will fail. The server administrator must configure it to send the full chain (using fullchain.pem rather than just the leaf certificate).
Self-Signed vs. CA-Signed Certificates:
Self-signed certificates are valid for internal testing but should never be used in production without explicit trust configuration. In production, always use certificates from a recognized public CA (Let's Encrypt, DigiCert, etc.).
Disabling SSL Verification (Not Recommended):
While Node.js allows disabling certificate verification with rejectUnauthorized: false, this should only be done temporarily in development:
const agent = new https.Agent({ rejectUnauthorized: false });Never use this in production as it removes all protection against MITM attacks.
Debugging Certificate Issues:
Enable verbose TLS debugging with:
NODE_DEBUG=tls node your-app.js 2>&1 | grep -i certCertificate Rotation:
For production systems, implement automatic certificate renewal (e.g., using Let's Encrypt with certbot or similar tools) to prevent expiration-related errors.
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