Skip to content
ecdsa.com
Ruby openssl (Net::HTTP, Faraday, HTTParty, RubyGems)Ruby

Error message

state=error: certificate verify failed (unable to get local issuer certificate)

Also printed as

  • OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:8443 state=error: certificate verify failed (unable to get local issuer certificate)

The full message is "SSL_connect returned=1 errno=0 peeraddr=<ip>:<port> state=error: certificate verify failed (unable to get local issuer certificate)"; peeraddr was present with openssl gem 3.0.1 to 4.0.2 in our tests. Faraday wraps it as Faraday::SSLError with the same message; libraries that call Net::HTTP directly pass OpenSSL::SSL::SSLError through.

Ruby openssl (Net::HTTP, Faraday, HTTParty, RubyGems) — OpenSSL::SSL::SSLError from SSL_connect

What it means

Ruby's OpenSSL binding (used by Net::HTTP and therefore by most Ruby HTTP clients) could not find the issuer of the server's certificate chain among the CAs it loads. The text in parentheses is OpenSSL's verify reason; everything before it describes where in SSL_connect the failure happened. Usually the CA is private or behind a proxy, or this Ruby looks at a CA bundle that does not contain it.

Why it happens

How to fix it

  1. 1.

    Install the root where Ruby's OpenSSL looks

    OpenSSL::X509::DEFAULT_CERT_FILE and DEFAULT_CERT_DIR show the paths; on Debian and Ubuntu, update-ca-certificates updates them.

    bash
    # Ruby's OpenSSL reads the system bundle (see its default paths)
    ruby -ropenssl -e 'p OpenSSL::X509::DEFAULT_CERT_FILE, OpenSSL::X509::DEFAULT_CERT_DIR'
    sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && sudo update-ca-certificates
  2. 2.

    Set SSL_CERT_FILE for the process

    OpenSSL honours SSL_CERT_FILE and SSL_CERT_DIR; point them at a bundle that contains the public roots and your CA.

    bash
    # One process / environment: point OpenSSL at a bundle with the needed roots
    export SSL_CERT_FILE=/etc/ssl/private-ca/corp-ca-bundle.pem
    ruby app.rb
  3. 3.

    Give one client its own store

    An OpenSSL::X509::Store with set_default_paths plus add_file keeps the public roots and adds the private CA, with full verification.

    ruby
    # One client: default roots plus a private CA
    store = OpenSSL::X509::Store.new
    store.set_default_paths
    store.add_file("/etc/ssl/private-ca/corp-root-ca.pem")
    
    http = Net::HTTP.new("api.example.com", 443)
    http.use_ssl = true
    http.cert_store = store
  4. 4.

    Faraday and other wrappers: pass ca_file or cert_store

    Most Ruby HTTP libraries accept ssl options that map to Net::HTTP's ca_file, ca_path or cert_store.

    ruby
    conn = Faraday.new("https://api.example.com", ssl: { ca_file: "/etc/ssl/private-ca/corp-root-ca.pem" })

Related errors

Browse the full signature error database