When developing web applications, integrating fintech payment gateways, or automating background API webhooks in Linux environments across Pakistan, cURL is an indispensable utility.
Whether running automated bash scripts, executing PHP curl_exec() requests, or consuming REST endpoints via Python’s requests library, developers rely on cURL to establish secure HTTPS connections.
However, few terminal errors are as cryptic and disruptive as:
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Or on newer OpenSSL 3.x installations:
curl: (35) OpenSSL/3.0.13: error:0A000410:SSL routines::sslv3 alert handshake failure
The error terminates your request before a single byte of HTTP data is transmitted. Payment callbacks fail, automated license checks abort, and API integrations halt.
What makes cURL Error 35 particularly confusing is that the destination website may load perfectly fine in a desktop browser like Google Chrome on the exact same computer!
Why does cURL fail when your browser succeeds?
The root cause lies in TLS Handshake Negotiation Mismatches. In this forensic engineering guide, we dissect the cryptographic mechanics of cURL Error 35, provide diagnostic OpenSSL commands, and demonstrate how to resolve cipher, protocol, and SECLEVEL constraints.
🔬 The Anatomy of cURL Error 35: Why Handshakes Fail
During the initial phase of an HTTPS connection, the client and server negotiate cryptographic parameters:
1. Client (cURL) sends ClientHello:
- "I support TLS 1.2 and TLS 1.3"
- "Here are the 15 cipher suites I am allowed to use"
- "Server Name Indication (SNI): api.yourdomain.pk"
2. Server inspects ClientHello:
- Checks its own enabled protocols (e.g., TLS 1.2/1.3)
- Checks its own configured cipher suites
3. CRITICAL DECISION POINT:
- If Client and Server have AT LEAST ONE shared cipher suite: Handshake succeeds!
- If Client and Server share ZERO mutually compatible ciphers:
Server abruptly sends: [TLS Alert: Handshake Failure (Level: Fatal, Code: 40)]
cURL throws: [curl: (35) sslv3 alert handshake failure]
(Note: Despite the reference to sslv3 in the legacy error string, this error has nothing to do with obsolete SSL 3.0; it is an internal OpenSSL error code constant representing a failure during TLS 1.2 or TLS 1.3 negotiation).
🔍 The 4 Most Common Causes of cURL Error 35
1. Modern Linux OpenSSL SECLEVEL=2 Restrictions
Modern Linux operating systems (Ubuntu 22.04 / 24.04, Debian 12, RHEL / Rocky Linux 9) ship with OpenSSL 3.0 configured with a strict default security level: @SECLEVEL=2.
- Under
SECLEVEL=2, OpenSSL strictly forbids RSA or Diffie-Hellman keys smaller than 2048 bits and bans SHA-1 signatures. - If your server’s cURL script attempts to communicate with a legacy banking gateway or government portal in Pakistan that still uses older 1024-bit primes or SHA-1 certificates, OpenSSL instantly aborts the handshake!
2. TLS Version Incompatibility
The remote server may enforce modern TLS 1.3 exclusively, while an outdated Linux container or legacy PHP runtime only supports TLS 1.1 or 1.2. Conversely, an archaic server may require TLS 1.0, which modern cURL has permanently disabled.
3. Missing Server Name Indication (SNI)
In multi-tenant hosting environments where hundreds of domains share a single IPv4 address, the server relies on SNI to determine which SSL certificate to present. Older cURL versions or raw socket scripts that do not pass the SNI hostname cause the server to present a default mismatching certificate, triggering an abort.
4. Outdated Local CA Root Store
If your local operating system’s Certificate Authority bundle (/etc/ssl/certs/ca-certificates.crt) is outdated, cURL cannot verify modern root certificates (such as the ISRG Root X1 for Let’s Encrypt).
🛠️ Step 1: Diagnose the Handshake with Detailed cURL Verbosity
To uncover the exact cryptographic failure point, run cURL with maximum verbose tracing (-Iv):
curl -Iv https://api.yourdomain.pk
Sample output pinpointing a cipher negotiation block:
* Connected to api.yourdomain.pk (103.151.xxx.xxx) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, handshake failure (552):
* OpenSSL/3.0.13: error:0A000410:SSL routines::sslv3 alert handshake failure
* Closing connection 0
curl: (35) OpenSSL/3.0.13: error:0A000410:SSL routines::sslv3 alert handshake failure
To probe supported TLS versions individually:
# Force TLS 1.2
curl -Iv --tlsv1.2 https://api.yourdomain.pk
# Force TLS 1.3
curl -Iv --tlsv1.3 https://api.yourdomain.pk
🛠️ Solution 1: Fixing Server-Side Cipher Incompatibilities (Nginx / Apache)
If you are the administrator of the destination server and your clients or API consumers are reporting cURL Error 35, your web server’s cipher suite string is overly restrictive.
For Nginx:
Open /etc/nginx/nginx.conf or your virtual host file:
# Support both modern TLS 1.2 and 1.3 with intermediate cipher compatibility
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
Reload Nginx:
sudo nginx -t && sudo systemctl reload nginx
For Apache (httpd):
Edit /etc/apache2/mods-available/ssl.conf (or /etc/httpd/conf.d/ssl.conf):
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES:!CAMELLIA:!PSK:!SRP
SSLHonorCipherOrder on
Reload Apache:
sudo systemctl reload apache2 # or httpd
🛠️ Solution 2: Client-Side Fixes (Updating CA Bundles & SECLEVEL)
If you are the client making outbound requests and receiving cURL Error 35:
1. Update Your Operating System Root CA Certificates
Ensure your operating system possesses current CA trust roots:
# On Ubuntu / Debian:
sudo apt update
sudo apt install -y ca-certificates openssl
sudo update-ca-certificates
# On AlmaLinux / Rocky Linux / RHEL:
sudo dnf install -y ca-certificates openssl
sudo update-ca-trust
2. Lower OpenSSL SECLEVEL for Legacy Gateway Integration (If Mandatory)
If your application must communicate with an archaic external server (such as an old legacy banking gateway) that uses legacy ciphers:
You can test with cURL by lowering the cipher security level in the command:
curl --ciphers 'DEFAULT@SECLEVEL=1' https://legacy-bank.example.pk
To enable this inside a PHP curl_setopt() request:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://legacy-bank.example.pk/api");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, "DEFAULT@SECLEVEL=1");
$response = curl_exec($ch);
curl_close($ch);
(Note: Lowering SECLEVEL=1 should only be done for specific isolated requests targeting known legacy partners, never globally for all outbound traffic).
🏆 Enterprise Cloud Infrastructure with Pre-Hardened TLS
Avoid cipher mismatches, broken OpenSSL dependencies, and API integration failures by building on modern cloud architecture:
- Deploy high-availability API microservices on Nextgen Cloud VPS in Pakistan featuring dedicated IPv4 addresses, pre-hardened OpenSSL stacks, automated HTTP/2 and HTTP/3 support, and low-latency PkIX peering.
- For financial institutions, e-commerce conglomerates, and organizations needing dedicated bare-metal server resources, automated hardware diagnostics, and 99.99% uptime SLAs, deploy on Nextgen bare-metal Dedicated Servers in Pakistan and international Dedicated Servers.
📚 Related SSL, Security & Linux Troubleshooting Guides
- How to Fix SEC_ERROR_OCSP_SERVER_ERROR in Firefox – Resolve CA responder timeouts and configure OCSP stapling.
- How to Fix ERR_SSL_WEAK_EPHEMERAL_DH_KEY – Harden Diffie-Hellman parameters against Logjam attacks.
- How to Fix ERR_CERT_DATE_INVALID in Browsers – Diagnose NTP time drift and validity windows.
Upgrade to Enterprise Cloud VPS in Pakistan
Say goodbye to cURL handshake failures, cipher negotiation drops, and API communication stalls. Nextgen delivers developer-first Cloud VPS and Bare-Metal Dedicated Servers pre-configured with modern TLS 1.3 and high-speed local routing.
