logo of Akmatori
21.09.2025

How to Fix SSL_do_handshake() failed error in NGINX

head-image

You’re running NGINX as a reverse proxy. Your app works locally, but when connecting to an upstream over HTTPS, you get this error in your logs:

2025/09/19 16:22:38 [error] 2400768#2400768: *14601717 SSL_do_handshake() failed (SSL: error:0A000410:SSL routines::ssl/tls alert handshake failure:SSL alert number 40) while SSL handshaking to upstream, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", upstream: "https://162.159.140.245:443/", host: "127.0.0.1:8081"

Let’s break this down and show you how to fix it.


What Does This Error Mean?

This error happens during the SSL handshake between NGINX and the upstream HTTPS server.

Specifically:

  • SSL_do_handshake() failed: The handshake couldn’t be completed.
  • alert handshake failure: The upstream server rejected the connection.
  • SSL alert number 40: This alert code means Handshake Failure.

💡 In short: NGINX tried to connect to an HTTPS upstream, but the server didn’t like something about the SSL/TLS negotiation.


Common Causes of SSL Handshake Failures in NGINX

Here are the most common reasons this happens:

1. TLS Version Mismatch

The upstream server might require TLS 1.2 or TLS 1.3, but NGINX could be using an older version.

2. Cipher Suite Incompatibility

The cipher suites supported by NGINX and the upstream server may not overlap.

3. Missing SNI

Some upstream servers require SNI (Server Name Indication). If not provided, the handshake fails.

4. Invalid Certificates or Trust Issues

If NGINX does not trust the upstream server's certificate, it may block the handshake.

5. Self-signed Certificate or Expired Cert

The upstream server may use a self-signed or expired cert. NGINX rejects these unless configured to allow them.


How to Fix It

Here are practical steps to resolve this issue:


✅ Step 1: Add proxy_ssl_server_name on;

Enable SNI for the proxy connection:

location / {
    proxy_pass https://162.159.140.245;
    proxy_ssl_server_name on;
}

This tells NGINX to send the Host name in the TLS handshake, which some CDNs or secure servers require.


✅ Step 2: Set the Correct proxy_ssl_name

You’re connecting to an IP, but the server expects a hostname (used in its SSL certificate).

Add this:

location / {
    proxy_pass https://162.159.140.245;
    proxy_ssl_server_name on;
    proxy_ssl_name example.com; # Replace with correct domain
}

This ensures NGINX presents the correct SNI in the TLS handshake.


✅ Step 3: Adjust TLS Protocols (if needed)

In some rare cases, you may need to tweak the TLS versions:

proxy_ssl_protocols TLSv1.2 TLSv1.3;

You can also specify allowed ciphers:

proxy_ssl_ciphers HIGH:!aNULL:!MD5;

✅ Step 4: Skip SSL Verification (for internal/test use only)

If you're dealing with a self-signed cert and you trust the upstream:

proxy_ssl_verify off;

⚠️ Use this only in dev/staging. In production, always validate SSL certs.


Testing the Connection Manually

You can test the SSL handshake using openssl:

openssl s_client -connect 162.159.140.245:443 -servername example.com

Check if the handshake succeeds and review the certificate chain.


Real-World Scenario: Proxying to a CDN or API

Many services like Cloudflare, AWS, or Gcore expect the correct Host and TLS SNI when accepting HTTPS requests.

If you're using NGINX to proxy traffic to such services, you must:

  • Enable proxy_ssl_server_name
  • Set proxy_ssl_name to the domain name
  • Ensure certificate trust is configured

Avoid On-Call Headaches with Akmatori

Tired of wasting time debugging production issues like SSL handshakes?

Try Akmatori, the AIOps platform designed to:

  • Catch and alert on SSL errors in real time
  • Automate incident resolution
  • Reduce alert fatigue and downtime

Akmatori helps DevOps teams stay ahead of issues before users notice.


Run Your NGINX Setups Globally with Gcore

Need powerful and affordable servers to deploy your NGINX proxies?

Use Gcore to spin up VMs and bare metal across global regions. With high-speed networking and strong uptime, Gcore is perfect for edge and reverse proxy deployments.

Check out Gcore's VM and server pricing.


Final Thoughts

The error:

SSL_do_handshake() failed (SSL alert number 40)

... means a failed TLS negotiation between NGINX and your upstream.

In most cases, the fix is simple:

  • Enable proxy_ssl_server_name on;
  • Set the correct proxy_ssl_name
  • Use valid SSL certs
  • Tune TLS versions and ciphers if needed

Follow the steps above, and you’ll restore secure upstream connectivity fast.


Got more NGINX errors to debug? Let us know! And don’t forget to check out:

  • Akmatori for smarter on-call automation
  • Gcore for high-speed, global infrastructure

Happy proxying!

Maximize your website or application's performance and reliability!