Logo
Skip to main content
Development
7 min read

NodeJS Error: CERT_HAS_EXPIRED

D

Divya Mahi

November 30, 2023 · Updated December 25, 2023

NodeJS Error_ CERT_HAS_EXPIRED

Navigating the "NodeJS Error: CERT_HAS_EXPIRED": Solutions and Best Practices

Introduction

In the modern web, secure communication is paramount, making SSL/TLS certificates a cornerstone of secure Node.js applications. However, developers often encounter the "NodeJS Error: CERT_HAS_EXPIRED," which occurs when a certificate used in the application or while accessing external resources has expired. This blog delves into understanding this error, exploring common scenarios, providing fixes, and discussing best practices to ensure your Node.js applications maintain secure, uninterrupted communication.

const https = require('https');

// ✅ Renew the SSL certificate (best solution)
// certbot renew  (if using Let's Encrypt)

// ✅ Temporary workaround: Skip certificate validation
const agent = new https.Agent({ rejectUnauthorized: false });

https.get('https://expired.example.com/api', { agent }, (res) => {
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => console.log(data));
});

// ✅ Check certificate expiry
const tls = require('tls');
const socket = tls.connect(443, 'example.com', () => {
  const cert = socket.getPeerCertificate();
  console.log('Expires:', cert.valid_to);
  socket.end();
});

Understanding the Error

The "NodeJS Error: CERT_HAS_EXPIRED" indicates that an SSL/TLS certificate used in a Node.js application has reached its expiration date. SSL/TLS certificates are used to establish secure connections, and they have a validity period after which they must be renewed. When Node.js tries to establish a secure connection using an expired certificate, this error is thrown.

const https = require('https');

// SSL certificate has expired on the server
https.get('https://expired.example.com/api', (res) => {
  // Error: CERT_HAS_EXPIRED
});

Diving Deeper

SSL/TLS certificates are time-bound for security reasons, ensuring that they are periodically renewed to maintain encryption standards. The error can arise both from certificates within your own application (e.g., for HTTPS servers) and from external resources your application is accessing (e.g., APIs with HTTPS).

Common Scenarios and Fixes with Example Code Snippets

Scenario 1: Expired Certificate in HTTPS Server

Problem: Setting up an HTTPS server with an expired certificate.

const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('expired-key.pem'),
  cert: fs.readFileSync('expired-cert.pem') // Certificate has expired
};

https.createServer(options, (req, res) => {
  res.end('Hello');
}).listen(443);

Solution: Renew the SSL/TLS certificate and update the server configuration.

const https = require('https');
const fs = require('fs');

// Use a renewed, valid certificate
const options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'), // Valid certificate
  // Optionally add CA chain
  ca: fs.readFileSync('ca-cert.pem')
};

https.createServer(options, (req, res) => {
  res.end('Hello');
}).listen(443, () => {
  console.log('HTTPS server running on port 443');
});

Scenario 2: HTTPS Request to a Server with an Expired Certificate

Problem: Making an HTTPS request to an external server with an expired certificate.

const https = require('https');

https.get('https://expired-cert-server.com/api', (res) => {
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => console.log(data));
}); // CERT_HAS_EXPIRED error

Solution: If you trust the server, you can bypass the certificate validation (not recommended for production).

const https = require('https');

https.get('https://expired-cert-server.com/api', (res) => {
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => console.log(data));
}).on('error', (err) => {
  if (err.code === 'CERT_HAS_EXPIRED') {
    console.error('Server certificate has expired. Contact the server admin.');
    // Do NOT use rejectUnauthorized: false in production!
  }
});

Scenario 3: Using Expired Client Certificates

Problem: Client-side certificates for authentication that have expired.

const https = require('https');
const fs = require('fs');

const options = {
  hostname: 'api.example.com',
  path: '/data',
  cert: fs.readFileSync('expired-client-cert.pem'),
  key: fs.readFileSync('client-key.pem'),
};

https.get(options, (res) => {
  console.log(res.statusCode);
});

Solution: Replace the client certificates with valid, updated ones.

const https = require('https');
const fs = require('fs');

// Renew client certificate and use valid one
const options = {
  hostname: 'api.example.com',
  path: '/data',
  cert: fs.readFileSync('valid-client-cert.pem'),
  key: fs.readFileSync('client-key.pem'),
  ca: fs.readFileSync('ca-cert.pem'),
};

https.get(options, (res) => {
  console.log('Status:', res.statusCode);
}).on('error', (err) => {
  if (err.code === 'CERT_HAS_EXPIRED') {
    console.error('Client certificate expired. Renew it.');
  }
});

Scenario 4: Certificates in Load Balancers or Reverse Proxies

Problem: Load balancers or reverse proxies with expired certificates.

// nginx.conf (conceptual)
// ssl_certificate /etc/ssl/expired-cert.pem;   <- Expired!
// ssl_certificate_key /etc/ssl/server-key.pem;

// Node.js behind the proxy gets certificate errors from clients
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello'));
app.listen(3000);

Solution: Update the certificates in these components, often involving server configuration outside of Node.js.

// Update nginx.conf with renewed certificate:
// ssl_certificate /etc/ssl/renewed-cert.pem;
// ssl_certificate_key /etc/ssl/server-key.pem;
// Then: sudo nginx -s reload

// Automate renewal with certbot
// sudo certbot renew --nginx

const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello'));
app.listen(3000, () => {
  console.log('Server running behind Nginx with valid SSL');
});

Scenario 5: Expired CA Certificates

Problem: Certificate Authority (CA) certificates used for validating connections might expire.

const https = require('https');
const fs = require('fs');

const options = {
  ca: fs.readFileSync('expired-ca-cert.pem'), // Expired CA
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),
};

https.createServer(options, (req, res) => {
  res.end('Secure');
}).listen(443);

Solution: Update the CA certificates bundle in your application or environment.

const https = require('https');
const fs = require('fs');

// Update the CA certificate chain
const options = {
  ca: fs.readFileSync('updated-ca-cert.pem'), // Valid CA certificate
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),
};

https.createServer(options, (req, res) => {
  res.end('Secure');
}).listen(443, () => {
  console.log('Server running with valid CA chain');
});

Scenario 6: Certificates in Third-party Modules

Problem: Using modules or SDKs that internally use expired certificates.

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 465,
  secure: true, // Fails if SMTP server cert is expired
  auth: { user: 'user@example.com', pass: 'password' },
});

Solution: Update the third-party modules or SDKs to the latest version where certificates might be updated.

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 465,
  secure: true,
  auth: { user: 'user@example.com', pass: 'password' },
  tls: {
    // Only for development/testing if cert is self-signed
    // rejectUnauthorized: false
    // In production: ensure SMTP server has valid certificate
  },
});

transporter.verify((err, success) => {
  if (err) {
    console.error('SMTP connection error:', err.message);
  } else {
    console.log('SMTP server is ready');
  }
});

Scenario 7: Certificates in Development Environments

Problem: Development environments often use self-signed certificates which can expire.

const https = require('https');
const fs = require('fs');

// Using a self-signed cert that was generated long ago
const options = {
  key: fs.readFileSync('dev-key.pem'),
  cert: fs.readFileSync('dev-cert.pem') // 1-year old self-signed cert
};

https.createServer(options, (req, res) => {
  res.end('Dev server');
}).listen(3443);

Solution: Generate new self-signed certificates for development use.

const https = require('https');
const { execSync } = require('child_process');
const fs = require('fs');

// Generate new self-signed cert for development
// openssl req -x509 -newkey rsa:4096 -keyout dev-key.pem -out dev-cert.pem -days 365 -nodes -subj "/CN=localhost"

const options = {
  key: fs.readFileSync('dev-key.pem'),
  cert: fs.readFileSync('dev-cert.pem')
};

https.createServer(options, (req, res) => {
  res.end('Dev server with fresh certificate');
}).listen(3443, () => {
  console.log('Development HTTPS server on port 3443');
});

Scenario 8: Handling Expired Certificates in External APIs

Problem: External APIs or services your application consumes might have expired certificates.

const axios = require('axios');

// External API's certificate expired
axios.get('https://partner-api.example.com/data')
  .then(res => console.log(res.data))
  .catch(err => console.log(err)); // CERT_HAS_EXPIRED

Solution: Contact the API provider to update their certificates or temporarily bypass certificate validation for testing.

const axios = require('axios');

axios.get('https://partner-api.example.com/data')
  .then(res => console.log(res.data))
  .catch(err => {
    if (err.code === 'CERT_HAS_EXPIRED') {
      console.error('Partner API certificate has expired.');
      console.error('Action: Contact partner to renew their SSL certificate.');
      // Fallback to cached data or alternative endpoint
    } else {
      console.error('API error:', err.message);
    }
  });

Strategies to Prevent Errors

Regular Monitoring: Regularly check the expiry dates of all certificates used in the application.

Automated Alerts: Set up automated alerts for certificate expiration.

Certificate Management Tools: Utilize tools and services for managing SSL/TLS certificate lifecycles.

Best Practices

Avoid Bypassing Certificate Validation: While it's a quick fix, it compromises security. Only use it temporarily in development or testing environments.

Use Certificate Rotation: Regularly rotate certificates even before they expire.

Keep Dependencies Updated: Regularly update any dependencies that might use SSL/TLS certificates.

Documentation: Keep a well-documented schedule of certificate expiry dates and renewal processes.

Conclusion

Handling the "NodeJS Error: CERT_HAS_EXPIRED" effectively requires a proactive approach to certificate management. By understanding the scenarios in which this error occurs and implementing the strategies and best practices outlined above, developers can ensure their Node.js applications remain secure and reliable. Remember, staying ahead of certificate expirations is key to maintaining the integrity of secure communications in your applications.

Development
D

Written by

Divya Mahi

Building innovative digital solutions at Poulima InfoTech. We specialize in web & mobile app development using React, Next.js, Flutter, and AI technologies.

Ready to Build Your Next Project?

Transform your ideas into reality with our expert development team. Let's discuss your vision.

Continue Reading

Related Articles