Skip to content

SSL/TLS Error Fixes -- How to Fix Common SSL/TLS Errors

DodaTech Updated 2026-06-22 6 min read

SSL/TLS errors like certificate expired and hostname mismatch break HTTPS connections instantly -- this guide shows you how to diagnose and fix the most common SSL certificate errors with command-line tools and server configuration fixes.

What You'll Learn

Why It Matters

SSL/TLS errors block users from accessing your website, trigger browser security warnings, and erode trust. Fixing them quickly is essential for any production web service.

Real-World Use

When your e-commerce site shows "Your connection is not private" or an API client fails with certificate verify failed, applying the correct SSL fix restores secure communication immediately.

Common SSL/TLS Errors Table

Error Message Cause Fix
certificate has expired SSL certificate past its validity date Renew the certificate with your CA or Let's Encrypt
Hostname mismatch Certificate CN/SAN does not match domain Re-issue certificate with correct domain names
self-signed certificate in chain Using a self-signed certificate without trust Add the CA certificate to the trust store or use a public CA
SSL: CERTIFICATE_VERIFY_FAILED Client does not trust the server certificate Install the CA bundle or disable verification (dev only)
handshake failure Protocol version or cipher mismatch Enable TLS 1.2+ and update cipher suites on both sides
certificate not yet valid System date is wrong or certificate future-dated Sync system clock with NTP or re-issue certificate with correct dates
unable to get local issuer certificate Intermediate CA certificate missing from chain Include full certificate chain in server configuration

Step-by-Step Fixes

Fix 1: Certificate Expired

# bad -- check certificate expiry
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
# notAfter=Jun 20 12:00:00 2025 GMT  (EXPIRED)
# fixed -- renew the certificate with certbot (Let's Encrypt)
sudo certbot renew --nginx
# Or force renewal
sudo certbot certonly --force-renewal -d example.com

# Verify the new expiry date
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
# notAfter=Jun 22 12:00:00 2026 GMT

Expected output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Renewing an existing certificate for example.com
Successfully renewed certificate for example.com

Fix 2: Hostname Mismatch

# bad -- check certificate CN and SANs
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject: CN"
# Subject: CN = wrong-domain.com
# X509v3 Subject Alternative Name:
#     DNS:wrong-domain.com
# fixed -- re-issue with correct domain
sudo certbot certonly --nginx -d example.com -d www.example.com

# Verify the SANs now include your domain
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
#     DNS:example.com, DNS:www.example.com

Expected output:

Requesting a certificate for example.com and www.example.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem

Fix 3: Self-Signed Certificate Not Trusted

# bad -- curl rejects self-signed cert
curl https://internal-api.example.com
# curl: (60) SSL certificate problem: self-signed certificate
# fixed -- add self-signed CA to trust store
# Option 1: Add the CA cert to system trust store (Linux)
sudo cp ca-cert.pem /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates

# Option 2: Use curl with the custom CA
curl --cacert /path/to/ca-cert.pem https://internal-api.example.com

# Option 3: Ignore verification (development only)
curl -k https://internal-api.example.com

Expected output:

{"status": "ok"}    # successful HTTPS request

Fix 4: Missing Intermediate Certificate

# bad -- verify the chain
openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null
# depth=0: CN = example.com (server cert only, incomplete chain)
# verify error: unable to get local issuer certificate
# fixed -- include full chain in server config
# Nginx: ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# Apache: SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

# Verify the complete chain is served
openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | openssl crl2pkcs7 -nocrl -certfile /dev/stdin | openssl pkcs7 -print_certs -text | grep "Subject:"

Expected output:

subject=CN = example.com
subject=C = US, O = Let's Encrypt, CN = R3
subject=O = Internet Security Research Group, CN = ISRG Root X1

Fix 5: Handshake Failure

# bad -- check supported protocols
openssl s_client -connect example.com:443 -tls1_1 -servername example.com 2>/dev/null
# 140735550713408:error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version
# fixed -- update server to support TLS 1.2 minimum
# Nginx configuration
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers HIGH:!aNULL:!MD5;

# Restart Nginx and verify
sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_2 -servername example.com 2>/dev/null

Expected output:

CONNECTED(00000003)
---
SSL handshake has read 4528 bytes and written 415 bytes
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256

SSL/TLS Error Diagnosis Flowchart

flowchart TD
    A[SSL/TLS Error] --> B{Error Type?}
    B -->|Certificate Expired| C[Check expiry date]
    C --> D[Renew with certbot or CA]
    B -->|Hostname Mismatch| E[Check CN and SANs]
    E --> F[Re-issue with correct domain]
    B -->|Self-Signed Not Trusted| G[Check trust store]
    G --> H[Add CA to system trust or use --cacert]
    B -->|Missing Intermediate| I[Check certificate chain]
    I --> J[Include fullchain.pem in config]
    B -->|Handshake Failure| K[Check protocol version]
    K --> L[Enable TLS 1.2+ and modern ciphers]
    B -->|Date Invalid| M[Check system clock]
    M --> N[Sync with NTP]
    D --> O[SSL Working]
    F --> O
    H --> O
    J --> O
    L --> O
    N --> O

Prevention Tips

  • Set up automatic certificate renewal with certbot and systemd timers
  • Monitor certificate expiry dates with tools like certwatch or UptimeRobot
  • Always include the full certificate chain (server cert + intermediates + root)
  • Use TLS 1.2 minimum and disable SSLv3, TLSv1.0, and TLSv1.1
  • Test your SSL configuration with ssllabs.com/ssltest or testssl.sh
  • Keep system clocks synchronized with NTP to avoid certificate validity errors
  • Use a certificate transparency monitor to get alerts before certificates expire

Practice Questions

  1. What does "SSL certificate has expired" mean and how do you fix it? Answer: The certificate's validity period has passed. Renew it with your certificate authority using certbot or your CA's renewal process.

  2. How do you fix a hostname mismatch SSL error? Answer: Re-issue the certificate with the correct domain names in the Subject Alternative Name (SAN) field. The certificate's CN or SAN must match the domain users visit.

  3. What causes an SSL handshake failure? Answer: The client and server could not agree on a protocol version or cipher suite. Enable TLS 1.2+ on the server and update cipher suites to modern standards.

  4. How do you fix "unable to get local issuer certificate"? Answer: The server is not sending the intermediate CA certificate. Configure the server to use the full certificate chain (fullchain.pem) instead of just the server certificate.

  5. Challenge: Write a bash script that checks all domains in a file for SSL certificate expiry, warns if expiry is within 30 days, and shows the number of days remaining for each domain. Answer:

    #!/bin/bash
    # usage: ./ssl-check.sh domains.txt
    DOMAINS_FILE="${1:-domains.txt}"
    WARN_DAYS=30
    
    while IFS= read -r domain; do
        [ -z "$domain" ] && continue
        expiry=$(echo | openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
        if [ -z "$expiry" ]; then
            echo "$domain: FAILED to connect"
            continue
        fi
        expiry_epoch=$(date -d "$expiry" +%s)
        now_epoch=$(date +%s)
        days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
        if [ "$days_left" -lt 0 ]; then
            echo "$domain: EXPIRED ($(( -days_left )) days ago)"
        elif [ "$days_left" -lt "$WARN_DAYS" ]; then
            echo "$domain: WARNING - $days_left days remaining"
        else
            echo "$domain: OK - $days_left days remaining"
        fi
    done < "$DOMAINS_FILE"
    

Quick Reference

Error Cause Quick Fix
Certificate expired Past validity date sudo certbot renew
Hostname mismatch CN/SAN does not match domain Re-issue cert with correct domain
Self-signed not trusted Not in trust store Add CA to system trust store
Missing intermediate Incomplete cert chain Use fullchain.pem in config
Handshake failure Protocol/cipher mismatch Enable TLS 1.2+ and modern ciphers
Certificate not yet valid Wrong system date sudo ntpdate pool.ntp.org

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro