This site is an archive and is no longer updated.
    Back to Guides
    OpenSSLCertificates

    OpenSSL Verify Certificate Chain: Complete Guide

    Learn how to verify SSL/TLS certificate chains with OpenSSL. This guide covers chain validation, debugging missing intermediates, and fixing common trust issues.

    12 min readDecember 2025
    LinkedInX
    OpenSSL Chain Verification Guide

    Quick Answer: Verify a Certificate Chain

    # Verify certificate against CA bundle
    openssl verify -CAfile ca-bundle.crt server.crt
    
    # Verify with intermediate certificates
    openssl verify -CAfile root-ca.crt -untrusted intermediate.crt server.crt
    
    # Verify remote server chain
    openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>&1 | \
      grep "Verify return code"

    ✅ Success: "server.crt: OK" or "Verify return code: 0 (ok)"

    Understanding Certificate Chains

    A certificate chain (or certificate path) links your server certificate to a trusted root CA through a series of intermediate certificates.

    Root CA Certificate

    Self-signed, stored in browser/OS trust store

    Intermediate CA Certificate(s)

    Signed by Root CA, signs server certs

    Server (Leaf) Certificate

    Your domain certificate, signed by Intermediate

    Important: Servers must send the complete chain (leaf + intermediates). Root certificates are NOT sent - clients already have them in their trust store.

    Verify Local Certificate Files

    Basic Verification

    # Verify against system CA certificates
    openssl verify server.crt
    
    # Verify against specific CA bundle
    openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt server.crt
    
    # Verify against your CA file
    openssl verify -CAfile root-ca.crt server.crt

    Verify with Intermediate Certificates

    # Using -untrusted for intermediate certificates
    openssl verify -CAfile root-ca.crt -untrusted intermediate.crt server.crt
    
    # Multiple intermediates
    openssl verify -CAfile root-ca.crt \
      -untrusted intermediate1.crt \
      -untrusted intermediate2.crt \
      server.crt

    Verify a Certificate Bundle

    # If you have a combined chain file
    cat intermediate.crt root-ca.crt > ca-chain.crt
    openssl verify -CAfile ca-chain.crt server.crt
    
    # Verify each certificate in the chain individually
    openssl crl2pkcs7 -nocrl -certfile chain.pem | \
      openssl pkcs7 -print_certs -noout

    Show Chain Details

    # Display chain verification details
    openssl verify -show_chain -CAfile ca-bundle.crt server.crt
    
    # Example output:
    # server.crt: OK
    # Chain:
    # depth=0: CN = example.com (untrusted)
    # depth=1: CN = DigiCert SHA2 Extended Validation Server CA
    # depth=2: CN = DigiCert High Assurance EV Root CA

    Verify Remote Server Chains

    Check Server Certificate Chain

    # Basic remote verification
    openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>&1 | \
      grep -E "(Verify return code|depth|verify)"
    
    # Successful output:
    # Verify return code: 0 (ok)

    View Full Chain from Server

    # Show all certificates sent by server
    openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null 2>&1
    
    # Count certificates in chain
    openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null 2>&1 | \
      grep -c "BEGIN CERTIFICATE"

    Verify Against Specific CA

    # Verify remote server against custom CA bundle
    openssl s_client -connect internal.example.com:443 \
      -servername internal.example.com \
      -CAfile /path/to/internal-ca.crt < /dev/null 2>&1 | \
      grep "Verify return code"
    
    # Verify with system certificates
    openssl s_client -connect example.com:443 \
      -servername example.com \
      -CApath /etc/ssl/certs < /dev/null

    Extract and Save Chain

    # Save complete chain from server
    echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | \
      sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > chain.pem
    
    # Extract individual certificates
    csplit -f cert- chain.pem '/-----BEGIN CERTIFICATE-----/' '{*}' 2>/dev/null
    
    # Verify saved chain
    openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt chain.pem

    Common Verification Errors

    OpenSSL verify returns numeric error codes. Here are the most common ones:

    CodeErrorCommon Cause
    0okChain verified successfully
    2unable to get issuer certificateMissing intermediate certificate
    10certificate has expiredCertificate past validity date
    18self signed certificateRoot CA not in trust store
    19self signed certificate in chainRoot CA in chain but not trusted
    20unable to get local issuer certificateIntermediate/root not in CAfile
    21unable to verify the first certificateNo matching issuer found
    26unsupported certificate purposeWrong key usage/extended key usage

    Fixing Chain Issues

    Error 20/21: Missing Intermediate

    error 20 at 0 depth lookup: unable to get local issuer certificate
    # 1. Find the issuer of your certificate
    openssl x509 -in server.crt -issuer -noout
    
    # 2. Download the intermediate certificate from your CA
    # (Usually provided with your certificate or on CA website)
    
    # 3. Create a chain file (order: leaf → intermediate → root)
    cat server.crt intermediate.crt > chain.pem
    
    # 4. Verify the chain
    openssl verify -CAfile root-ca.crt chain.pem
    
    # 5. Configure your web server with the chain file

    Error 10: Certificate Expired

    # Check which certificate in chain is expired
    for cert in *.crt; do
      echo "=== $cert ==="
      openssl x509 -in "$cert" -dates -noout
    done
    
    # Check if intermediate/root is expired (common issue)
    openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \
      awk '/BEGIN CERT/,/END CERT/{print}' | \
      while read line; do
        echo "$line"
      done | openssl x509 -dates -noout

    Error 18/19: Self-Signed Certificate

    # Add self-signed root to your trust store
    # For testing only:
    openssl verify -CAfile self-signed-root.crt server.crt
    
    # For system-wide trust (Ubuntu/Debian):
    sudo cp self-signed-root.crt /usr/local/share/ca-certificates/
    sudo update-ca-certificates
    
    # For system-wide trust (RHEL/CentOS):
    sudo cp self-signed-root.crt /etc/pki/ca-trust/source/anchors/
    sudo update-ca-trust

    Correct Chain Order

    Correct order (Nginx/Apache):

    1. Server (leaf) certificate
    2. Intermediate CA certificate(s)
    3. Root CA (optional, usually not included)
    # Create properly ordered chain file
    cat server.crt intermediate.crt > fullchain.pem
    
    # Verify order
    openssl crl2pkcs7 -nocrl -certfile fullchain.pem | \
      openssl pkcs7 -print_certs -text -noout | \
      grep "Subject:"
    
    # Should show:
    # Subject: CN=example.com (your server)
    # Subject: CN=DigiCert... (intermediate)
    # Subject: CN=DigiCert... (root, if included)

    Troubleshooting Guide

    Chain works locally but fails on clients

    Your server might not be sending the full chain:

    # Count certificates sent by server
    openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \
      grep -c "BEGIN CERTIFICATE"
    
    # Should be 2+ (server + at least 1 intermediate)
    # If only 1, you need to configure your server with the chain file

    Verification works but browsers show warnings

    Check for hostname mismatch or expired intermediates:

    # Verify hostname matches
    openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | \
      openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
    
    # Check all dates in chain
    openssl s_client -connect example.com:443 -showcerts 2>/dev/null | \
      awk '/BEGIN CERT/,/END CERT/{print}' > /tmp/chain.pem
    openssl crl2pkcs7 -nocrl -certfile /tmp/chain.pem | \
      openssl pkcs7 -print_certs -text | grep -E "(Subject:|Not After)"

    Corporate proxy/firewall issues

    Some proxies intercept TLS and present their own certificates:

    # Check if certificate is from expected issuer
    openssl s_client -connect example.com:443 2>/dev/null | \
      openssl x509 -issuer -noout
    
    # If issuer is your company, you're behind a TLS-inspecting proxy
    # You may need to add the proxy's CA to your trust store

    Best Practices Checklist

    • Always include intermediate certificates in server config
    • Use the correct chain order: leaf → intermediate → root
    • Never include the root CA in the chain (clients have it)
    • Test chain with multiple browsers and OpenSSL
    • Monitor certificate expiration dates (including intermediates)
    • Verify chain after any certificate renewal
    • Use SSL testing tools (SSL Labs, etc.) for production
    • Keep intermediate certificates updated from your CA

    Frequently Asked Questions

    Why does OpenSSL say "unable to get local issuer certificate"?

    This means OpenSSL can't find the issuing CA certificate. Use -CAfile to specify your CA bundle, or -untrusted to provide intermediate certificates.

    Should I include the root certificate in my chain?

    No. Clients already have root certificates in their trust store. Including it wastes bandwidth and can cause issues with some clients. Only include leaf + intermediates.

    What order should certificates be in the chain file?

    Start with your server certificate (leaf), then add intermediates in order up to (but not including) the root. Many servers require this specific order.

    How do I get intermediate certificates?

    Your CA provides them when issuing your certificate. You can also download them from the Authority Information Access (AIA) URL in your certificate.

    Why does my chain work in Chrome but fail in OpenSSL?

    Browsers may cache intermediates from other sites (AIA fetching). OpenSSL doesn't - it only uses what you provide. Always test with OpenSSL to ensure complete chains.

    How do I verify a self-signed certificate?

    Use the certificate itself as the CA: openssl verify -CAfile self-signed.crt self-signed.crt. For testing, you can add it to your trust store.

    Related Resources