Table of contents

How do I verify SSL certificates when making a request with Requests?

SSL certificate verification is a critical security feature in Python's Requests library that protects against man-in-the-middle attacks. This guide covers how to manage SSL certificate verification for different scenarios.

Default SSL Verification

By default, Requests automatically verifies SSL certificates for HTTPS requests using the system's trusted Certificate Authority (CA) bundle:

import requests

try:
    response = requests.get('https://httpbin.org/get')
    print(f"Status: {response.status_code}")
    print("SSL certificate verified successfully")
except requests.exceptions.SSLError as e:
    print(f"SSL verification failed: {e}")

When SSL verification fails, Requests raises a requests.exceptions.SSLError.

Checking Certificate Details

You can inspect SSL certificate information in your responses:

import requests

response = requests.get('https://httpbin.org/get')

# Access SSL info through the underlying urllib3 response
if hasattr(response.raw, '_original_response'):
    ssl_info = response.raw._original_response.peer_cert
    print(f"Certificate subject: {ssl_info.get('subject', 'N/A')}")

Disabling SSL Verification (Not Recommended)

⚠️ Security Warning: Only disable SSL verification in development or when you fully understand the security implications.

import requests
import urllib3

# Disable SSL warnings when verification is disabled
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

response = requests.get('https://self-signed.badssl.com/', verify=False)
print(f"Status: {response.status_code}")

Session-Level SSL Configuration

Configure SSL settings for all requests in a session:

import requests

session = requests.Session()
session.verify = False  # Apply to all requests in this session

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

Using Custom CA Bundles

For corporate environments or custom certificates, specify a custom CA bundle file:

import requests

# Using a custom CA bundle file
response = requests.get(
    'https://example.com',
    verify='/path/to/custom-ca-bundle.pem'
)

# Using certifi's CA bundle explicitly
import certifi
response = requests.get(
    'https://example.com',
    verify=certifi.where()
)

Creating a Custom CA Bundle

import requests
import ssl
import os

# Get the default CA bundle location
ca_bundle_path = requests.certs.where()
print(f"Default CA bundle: {ca_bundle_path}")

# Combine default bundle with custom certificate
custom_ca_path = '/tmp/custom-ca-bundle.pem'
with open(custom_ca_path, 'w') as custom_bundle:
    # Copy default certificates
    with open(ca_bundle_path, 'r') as default_bundle:
        custom_bundle.write(default_bundle.read())

    # Add your custom certificate
    with open('/path/to/your-custom-cert.pem', 'r') as custom_cert:
        custom_bundle.write('\n')
        custom_bundle.write(custom_cert.read())

# Use the custom bundle
response = requests.get('https://your-custom-site.com', verify=custom_ca_path)

Client Certificate Authentication

For mutual TLS authentication, provide client certificates:

import requests

# Client certificate and private key as separate files
response = requests.get(
    'https://client-cert-required.example.com',
    cert=('/path/to/client.crt', '/path/to/client.key')
)

# Client certificate with password-protected private key
response = requests.get(
    'https://client-cert-required.example.com',
    cert=('/path/to/client.crt', '/path/to/client.key', 'key_password')
)

# Combined certificate and key in single file
response = requests.get(
    'https://client-cert-required.example.com',
    cert='/path/to/client.pem'
)

Client Certificate with Custom CA

import requests

response = requests.get(
    'https://mutual-tls.example.com',
    cert=('/path/to/client.crt', '/path/to/client.key'),
    verify='/path/to/custom-ca.pem'
)

Advanced SSL Configuration

Using SSL Context

For fine-grained SSL control, create a custom SSL context:

import requests
import ssl
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context

class CustomSSLAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context()
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE
        # Add custom SSL settings here
        kwargs['ssl_context'] = context
        return super().init_poolmanager(*args, **kwargs)

session = requests.Session()
session.mount('https://', CustomSSLAdapter())

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

Troubleshooting SSL Errors

Common SSL Error Types

import requests
from requests.exceptions import SSLError, ConnectionError

def make_secure_request(url):
    try:
        response = requests.get(url, timeout=10)
        return response
    except SSLError as e:
        if "certificate verify failed" in str(e):
            print(f"Certificate verification failed for {url}")
            print("Possible causes:")
            print("- Self-signed certificate")
            print("- Expired certificate")
            print("- Incorrect system time")
        elif "hostname" in str(e):
            print(f"Hostname mismatch for {url}")
        else:
            print(f"SSL Error: {e}")
    except ConnectionError as e:
        print(f"Connection error: {e}")

    return None

# Test with different sites
response = make_secure_request('https://expired.badssl.com/')

Debugging SSL Issues

import requests
import ssl
import socket

def debug_ssl_connection(hostname, port=443):
    """Debug SSL connection details"""
    try:
        # Create SSL context
        context = ssl.create_default_context()

        # Connect and get certificate info
        with socket.create_connection((hostname, port)) as sock:
            with context.wrap_socket(sock, server_hostname=hostname) as ssock:
                cert = ssock.getpeercert()
                print(f"Certificate for {hostname}:")
                print(f"  Subject: {cert.get('subject')}")
                print(f"  Issuer: {cert.get('issuer')}")
                print(f"  Version: {cert.get('version')}")
                print(f"  Serial: {cert.get('serialNumber')}")
                print(f"  Not Before: {cert.get('notBefore')}")
                print(f"  Not After: {cert.get('notAfter')}")

    except Exception as e:
        print(f"SSL debug failed: {e}")

# Debug SSL for a specific site
debug_ssl_connection('httpbin.org')

Updating Certificate Bundle

import requests
import certifi

# Check current certifi version and update if needed
print(f"Current certifi version: {certifi.__version__}")
print(f"CA bundle location: {certifi.where()}")

# Force update certificates (requires pip install --upgrade certifi)
response = requests.get('https://httpbin.org/get')
print(f"Request successful: {response.status_code == 200}")

Best Practices

1. Always Use SSL Verification in Production

import os
import requests

# Environment-based SSL configuration
VERIFY_SSL = os.getenv('VERIFY_SSL', 'true').lower() == 'true'

response = requests.get('https://api.example.com', verify=VERIFY_SSL)

2. Handle SSL Errors Gracefully

import requests
from requests.exceptions import SSLError
import time

def robust_request(url, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, timeout=10)
            return response
        except SSLError as e:
            print(f"SSL error on attempt {attempt + 1}: {e}")
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # Exponential backoff
            else:
                raise
        except Exception as e:
            print(f"Other error: {e}")
            raise

response = robust_request('https://httpbin.org/get')

3. Log SSL Configuration

import requests
import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

def logged_request(url, **kwargs):
    verify_setting = kwargs.get('verify', True)
    cert_setting = kwargs.get('cert', None)

    logger.info(f"Making request to {url}")
    logger.info(f"SSL verification: {verify_setting}")
    logger.info(f"Client certificate: {'Yes' if cert_setting else 'No'}")

    return requests.get(url, **kwargs)

response = logged_request('https://httpbin.org/get')

Summary

  • Default behavior: SSL verification is enabled and uses system CA bundle
  • Custom CA bundles: Use verify='/path/to/ca-bundle.pem' for custom certificates
  • Client certificates: Use cert=('cert.pem', 'key.pem') for mutual TLS
  • Disable verification: Use verify=False only in development environments
  • Troubleshooting: Check certificate validity, system time, and CA bundle updates

Always prioritize security by keeping SSL verification enabled in production environments. Only disable or customize SSL settings when you have a clear understanding of the security implications and requirements.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon