cURL Ignore SSL Certificate Errors: Complete Guide for Proxy Users

cURL Ignore SSL Certificate Errors: Complete Guide for Proxy Users

SSL certificate errors are among the most common issues when using cURL with proxies, corporate networks, or development environments. While skipping SSL verification is sometimes necessary, it is important to understand the security implications and use proper fixes when possible. This guide covers all SSL-related cURL options with a focus on proxy configurations.

Quick Fix: The -k Flag

The fastest way to bypass SSL certificate errors in cURL:

curl -k https://example.com
# or the long form
curl --insecure https://example.com

Warning: This disables ALL certificate verification, making your connection vulnerable to man-in-the-middle attacks. Use this only for testing, never in production.

Common SSL Errors in cURL

Error Messages and Their Meanings

ErrorMeaningCommon Cause
curl: (60) SSL certificate problem: unable to get local issuer certificateCA bundle missing/outdatedMissing root CA certificates
curl: (60) SSL certificate problem: self-signed certificateServer uses self-signed certDev/staging environments
curl: (60) SSL certificate problem: certificate has expiredCertificate expiredServer admin hasn’t renewed
curl: (35) SSL connect errorSSL handshake failureTLS version mismatch, proxy interference
curl: (51) SSL: certificate subject name does not match target host nameHostname mismatchWrong domain, proxy MITM
curl: (58) unable to set private key fileClient cert issueWrong key file or format

SSL Issues with Proxy Servers

Proxy servers are one of the most common sources of SSL errors. Understanding why helps you fix them properly.

Why Proxies Cause SSL Errors

  1. SSL Interception (MITM Proxies): Corporate proxies often decrypt and re-encrypt HTTPS traffic using their own CA certificate
  2. Self-signed proxy certificates: Many proxy servers use self-signed certificates
  3. Certificate chain issues: Proxy may not forward the full certificate chain
  4. TLS version mismatches: Proxy may support different TLS versions than the target server

Using cURL with HTTP Proxies (No SSL Issues)

# HTTP CONNECT proxy - SSL is end-to-end, no proxy SSL issues
curl -x http://proxy:8080 https://example.com

Using cURL with HTTPS Proxies

# HTTPS proxy - requires trusting the proxy's certificate
curl -x https://proxy:8443 https://example.com

# If proxy has self-signed cert, skip proxy SSL verification
curl --proxy-insecure -x https://proxy:8443 https://example.com

Separate Flags for Proxy vs Server SSL

cURL distinguishes between proxy SSL and server SSL verification:

# Skip SSL verification for the proxy only (keep server verification)
curl --proxy-insecure -x https://proxy:8443 https://example.com

# Skip SSL verification for the target server only
curl -k -x http://proxy:8080 https://example.com

# Skip both (least secure)
curl -k --proxy-insecure -x https://proxy:8443 https://example.com

Proper SSL Fixes (Instead of -k)

Fix 1: Update CA Certificate Bundle

# macOS
brew install curl
# or update certificates
/usr/libexec/security_certificates

# Ubuntu/Debian
sudo apt-get update && sudo apt-get install ca-certificates
sudo update-ca-certificates

# CentOS/RHEL
sudo yum install ca-certificates
sudo update-ca-trust

# Specify CA bundle explicitly
curl --cacert /etc/ssl/certs/ca-certificates.crt https://example.com

Fix 2: Add Custom CA Certificate

For corporate proxies or internal services with custom CAs:

# Download the CA certificate
openssl s_client -connect proxy:8443 -showcerts </dev/null 2>/dev/null | \
  openssl x509 -outform PEM > proxy-ca.crt

# Use it with cURL
curl --cacert proxy-ca.crt https://example.com

# Or add to system trust store (Ubuntu)
sudo cp proxy-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# Or add to system trust store (macOS)
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain proxy-ca.crt

Fix 3: Specify CA Directory

curl --capath /etc/ssl/certs/ https://example.com

Fix 4: Set CA Bundle via Environment Variable

export CURL_CA_BUNDLE=/path/to/custom-ca-bundle.crt
curl https://example.com  # Uses custom bundle automatically

TLS Version Control

Sometimes SSL errors stem from TLS version incompatibilities:

# Force TLS 1.2
curl --tlsv1.2 https://example.com

# Force TLS 1.3
curl --tlsv1.3 https://example.com

# Set maximum TLS version
curl --tls-max 1.2 https://example.com

# Show which TLS version was negotiated
curl -v https://example.com 2>&1 | grep "SSL connection"

Debugging SSL Issues

Verbose SSL Output

curl -v https://example.com 2>&1 | grep -A 20 "SSL"

Show Full Certificate Chain

curl -vvv https://example.com 2>&1 | grep -E "subject:|issuer:|expire"

Using OpenSSL for Diagnosis

# Check certificate details
openssl s_client -connect example.com:443 -showcerts

# Through a proxy
openssl s_client -connect example.com:443 -proxy proxy:8080

# Check certificate expiry
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -dates

Python SSL Debugging Script

import subprocess
import ssl
import socket
import json

def diagnose_ssl(host, port=443, proxy=None):
    """Diagnose SSL certificate issues."""
    print(f"Diagnosing SSL for {host}:{port}")

    # Test with cURL
    cmd = ["curl", "-v", "--connect-timeout", "10"]
    if proxy:
        cmd.extend(["-x", proxy])
    cmd.append(f"https://{host}")

    result = subprocess.run(cmd, capture_output=True, text=True)

    # Parse SSL info from verbose output
    ssl_lines = [
        line for line in result.stderr.split("\n")
        if any(kw in line.lower() for kw in ["ssl", "tls", "certificate", "issuer", "subject"])
    ]

    print("\n--- SSL Connection Details ---")
    for line in ssl_lines:
        print(line.strip())

    # Check if it failed
    if result.returncode != 0:
        print(f"\n--- Error (exit code {result.returncode}) ---")
        error_lines = [l for l in result.stderr.split("\n") if "error" in l.lower()]
        for line in error_lines:
            print(line.strip())

        print("\n--- Suggested Fixes ---")
        if "unable to get local issuer certificate" in result.stderr:
            print("1. Update CA certificates: sudo update-ca-certificates")
            print("2. Use --cacert to specify CA bundle")
        elif "self-signed certificate" in result.stderr:
            print("1. Add the CA cert to your trust store")
            print("2. Use --cacert with the self-signed cert")
        elif "certificate has expired" in result.stderr:
            print("1. Contact the server administrator")
            print("2. Use -k for testing only (not production)")
        elif "SSL connect error" in result.stderr:
            print("1. Try --tlsv1.2 or --tlsv1.3")
            print("2. Check if proxy supports SSL passthrough")
    else:
        print("\nSSL connection successful!")

# Usage
diagnose_ssl("example.com")
diagnose_ssl("example.com", proxy="http://proxy:8080")

Handling Self-Signed Certificates Properly

Generate and Trust a Self-Signed Certificate

# Generate self-signed cert (for testing)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -days 365 -nodes -subj "/CN=localhost"

# Use with cURL by specifying the cert as CA
curl --cacert cert.pem https://localhost:8443/api

Pin a Specific Certificate

For maximum security, pin the exact certificate you expect:

# Get the certificate's public key hash
openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -pubkey -noout | \
  openssl pkey -pubin -outform DER | \
  openssl dgst -sha256 -binary | base64

# Use certificate pinning in cURL
curl --pinnedpubkey "sha256//YhKJG3VwXBhYJSQROg..." https://example.com

Configuration File for SSL Settings

Create a .curlrc file for persistent SSL settings:

# ~/.curlrc

# Custom CA bundle
cacert = /path/to/custom-ca-bundle.crt

# Force minimum TLS 1.2
tlsv1.2

# Proxy with its own CA
# proxy-cacert = /path/to/proxy-ca.crt

SSL Settings in Python (requests)

import requests

# Equivalent of curl -k
response = requests.get("https://example.com", verify=False)

# Specify custom CA bundle
response = requests.get("https://example.com", verify="/path/to/ca-bundle.crt")

# With proxy and custom CA
response = requests.get(
    "https://example.com",
    verify="/path/to/ca-bundle.crt",
    proxies={"https": "http://proxy:8080"}
)

# Client certificate (mTLS)
response = requests.get(
    "https://example.com",
    cert=("/path/to/client.crt", "/path/to/client.key"),
    verify="/path/to/ca-bundle.crt"
)

SSL Error Troubleshooting Flowchart

SSL Certificate Error
├── "unable to get local issuer certificate"
│   ├── Update CA certificates (apt/yum)
│   ├── Use --cacert with correct CA bundle
│   └── Set CURL_CA_BUNDLE environment variable
├── "self-signed certificate"
│   ├── Add cert to system trust store
│   ├── Use --cacert with the self-signed cert
│   └── Use -k for testing only
├── "certificate has expired"
│   ├── Contact server admin to renew
│   └── Check system clock (wrong date causes this)
├── "SSL connect error" (error 35)
│   ├── Try --tlsv1.2 or --tlsv1.3
│   ├── Check proxy SSL configuration
│   └── Verify firewall isn't blocking
└── "subject name does not match"
    ├── Verify you're connecting to the right host
    ├── Check if proxy is intercepting (MITM)
    └── Use --resolve to override DNS

FAQ

Is it safe to use curl -k in production?

No. The -k flag disables all SSL certificate verification, making your connection vulnerable to man-in-the-middle attacks. Any attacker on the network path could intercept and modify your traffic. In production, always fix the root cause by updating CA certificates, adding custom CA certs to your trust store, or using --cacert to specify the correct CA bundle.

Why do I get SSL errors when using a proxy?

Proxies cause SSL errors for several reasons: HTTPS proxies may use self-signed certificates, corporate proxies often perform SSL interception (MITM) with their own CA, and some proxies don’t properly forward certificate chains. Use --proxy-insecure for proxy-only SSL issues, or add the proxy’s CA certificate to your trust store with --cacert.

How do I fix “unable to get local issuer certificate” in cURL?

This error means cURL cannot find the CA certificate bundle to verify the server’s certificate. Fix it by: (1) updating system CA certificates with sudo update-ca-certificates, (2) specifying the CA bundle path with curl --cacert /path/to/ca-bundle.crt URL, or (3) setting the CURL_CA_BUNDLE environment variable to point to your CA bundle file.

What is the difference between –insecure and –proxy-insecure?

--insecure (or -k) skips SSL verification for the connection to the target server. --proxy-insecure skips SSL verification only for the connection to the HTTPS proxy server. You can use them independently: --proxy-insecure still verifies the target server’s certificate, and -k still verifies the proxy’s certificate. Use --proxy-insecure when your proxy has a self-signed cert but you still want to verify the target server.

How do I check which TLS version cURL is using?

Run curl -v https://example.com 2>&1 | grep "SSL connection" to see the negotiated TLS version and cipher suite. You can force a specific version with --tlsv1.2 or --tlsv1.3. To see all supported TLS versions, run curl --version and check the SSL library version listed.


Related Reading

Scroll to Top