# How to Fix Python SSL Certificate Verify Failed

SSL certificate verification errors occur when Python cannot validate the SSL certificate of a remote server. This guide covers common causes and solutions.

Error Patterns

requests Library

text
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: /api/data (Caused by SSLError(
SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)')))

urllib Library

text
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)>

httpx Library

text
httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: self signed certificate

aiohttp Library

text
aiohttp.client.ClientConnectorCertificateError:
Cannot connect to host example.com:443 ssl:True
[SSLCertVerificationError: (1, 'certificate verify failed')]

Common Causes

  1. 1.Self-signed certificates - Server uses certificate not signed by trusted CA
  2. 2.Expired certificates - Server certificate has expired
  3. 3.Missing CA bundle - Python cannot find root certificates
  4. 4.Corporate proxy - Proxy intercepts SSL with custom certificate
  5. 5.Certificate chain incomplete - Server missing intermediate certificates
  6. 6.System clock incorrect - Time skew causes validation failure

Diagnosis Steps

Step 1: Check Certificate with OpenSSL

bash
openssl s_client -connect example.com:443 -showcerts

Step 2: Verify System Time

bash
date  # Linux/macOS
# or
Get-Date  # Windows PowerShell

Step 3: Check Certificate Details

bash
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Step 4: Test URL in Browser

Open the URL in a browser and check for certificate warnings.

Solutions

Solution 1: Update CA Certificates

```bash # macOS (after Homebrew Python installation) /Applications/Python\ 3.x/Install\ Certificates.command

# Or run this Python script import certifi import ssl print(ssl.get_default_verify_paths()) print(certifi.where()) ```

```bash # Linux (Ubuntu/Debian) sudo apt-get update && sudo apt-get install ca-certificates sudo update-ca-certificates

# Linux (RHEL/CentOS) sudo yum update ca-certificates sudo update-ca-trust ```

bash
# Update certifi package
pip install --upgrade certifi

Solution 2: Disable Verification (Development Only)

WARNING: Only use for development/testing, never in production

```python import requests

# Disable SSL verification (NOT RECOMMENDED for production) response = requests.get('https://example.com', verify=False)

# Suppress warnings import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) response = requests.get('https://example.com', verify=False) ```

Solution 3: Specify Custom CA Bundle

```python import requests

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

# Or set environment variable import os os.environ['REQUESTS_CA_BUNDLE'] = '/path/to/ca-bundle.crt' os.environ['SSL_CERT_FILE'] = '/path/to/ca-bundle.crt' ```

Solution 4: Handle Self-Signed Certificates

```python import requests

# For self-signed certificate response = requests.get( 'https://self-signed.example.com', verify='/path/to/server.crt' )

# Or use certifi with custom cert import certifi import requests

# Add custom cert to certifi bundle # Then use normally response = requests.get('https://internal.example.com') ```

Solution 5: Corporate Proxy Certificates

```python import os import requests

# Set proxy certificate os.environ['REQUESTS_CA_BUNDLE'] = '/path/to/corporate-proxy-ca.pem' os.environ['HTTP_PROXY'] = 'http://proxy.company.com:8080' os.environ['HTTPS_PROXY'] = 'http://proxy.company.com:8080'

response = requests.get('https://example.com') ```

Solution 6: Fix urllib SSL Issues

```python import ssl import urllib.request

# Create SSL context ssl_context = ssl.create_default_context() ssl_context.load_verify_locations('/path/to/ca-bundle.crt')

# Use with urlopen with urllib.request.urlopen('https://example.com', context=ssl_context) as response: content = response.read() ```

Solution 7: Async Libraries (aiohttp)

```python import ssl import aiohttp

ssl_context = ssl.create_default_context() ssl_context.load_verify_locations('/path/to/ca-bundle.crt')

async with aiohttp.ClientSession() as session: async with session.get('https://example.com', ssl=ssl_context) as response: content = await response.text() ```

Solution 8: httpx Library

```python import httpx

# Custom verify response = httpx.get('https://example.com', verify='/path/to/ca-bundle.crt')

# Or with client with httpx.Client(verify='/path/to/ca-bundle.crt') as client: response = client.get('https://example.com') ```

Extract and Use Server Certificate

Extract Certificate from Server

bash
# Get certificate from server
openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM > server.crt

Use Extracted Certificate

```python import requests

response = requests.get( 'https://example.com', verify='server.crt' ) ```

Combine Multiple Certificates

bash
# Create a custom CA bundle
cat /etc/ssl/certs/ca-certificates.crt > custom-ca-bundle.crt
cat corporate-proxy.pem >> custom-ca-bundle.crt
cat self-signed-server.crt >> custom-ca-bundle.crt
python
import os
os.environ['REQUESTS_CA_BUNDLE'] = '/path/to/custom-ca-bundle.crt'

Prevention Tips

  1. 1.Keep certifi updated: pip install --upgrade certifi
  2. 2.Use proper certificates in production
  3. 3.Set up certificate rotation before expiry
  4. 4.Monitor certificate expiry with automation

```python # Check certificate expiry import ssl import socket from datetime import datetime

def check_cert_expiry(hostname, port=443): context = ssl.create_default_context() with socket.create_connection((hostname, port)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as ssock: cert = ssock.getpeercert() expiry = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z') days_left = (expiry - datetime.now()).days return days_left

days = check_cert_expiry('example.com') print(f"Certificate expires in {days} days") ```

  • ConnectionRefusedError - Server not accepting connections
  • socket.timeout - Connection timed out
  • CertificateError - Hostname mismatch