Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

Why Does Python Fail to Verify SSL Certificates? Common Causes and Practical Fixes

Have you ever run a Python script that works perfectly on your local machine but fails when it tries to connect to an HTTPS API? You might see an error similar to this: requests.exceptions.SSLError: HTTPSConnectionPool

Have you ever run a Python script that works perfectly on your local machine but fails when it tries to connect to an HTTPS API?

You might see an error similar to this:

requests.exceptions.SSLError:
HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: /
(Caused by SSLError(
SSLCertVerificationError:
certificate verify failed
))

This error can be frustrating, especially when the website opens normally in your browser.

But why does Python reject an SSL certificate that your browser appears to trust?

The answer usually comes down to how Python verifies certificate chains and which Certificate Authorities (CAs) its environment trusts.

Let's look at the common causes and how to troubleshoot them.

1. Why Does Python Reject an SSL Certificate?

When Python connects to an HTTPS website, it checks the server's TLS certificate to establish that it is connecting to the intended server and that the certificate chains back to a trusted CA.

Several things can cause this verification to fail:

The certificate has expired.

The server isn't providing the complete certificate chain.

The local CA certificate bundle is outdated.

The certificate was issued by a private or untrusted CA.

A corporate proxy is intercepting HTTPS connections.

The application is using an incorrect or incomplete CA bundle.

The important thing to understand is that a browser and a Python application may use different certificate trust stores.

Therefore, a website working in Chrome doesn't necessarily mean that a Python script will trust its certificate.

2. Check Whether the Problem Is With Python or the Server

Before changing your Python configuration, check the certificate presented by the server.

You can use OpenSSL:

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

Replace example.com with the hostname you're trying to access.

Look for the verification result near the bottom of the output.

For example:

Verify return code: 0 (ok)

This indicates that OpenSSL successfully verified the certificate chain using its configured trust store.

If you see a verification error, investigate the certificate chain, hostname, expiry, and trusted CA configuration.

Note: OpenSSL and Python may use different CA bundles, so a successful OpenSSL check doesn't automatically prove that Python will succeed.

3. Update Python's Certificate Bundle

One common cause is an outdated CA certificate bundle.

The Python requests library uses trusted CA certificates to verify HTTPS connections. It commonly uses the certifi package to provide a CA bundle.

First, update the relevant packages:

python -m pip install --upgrade requests certifi

Next, check which CA bundle certifi is using:

import certifi

print(certifi.where())

This prints the path to the CA certificate bundle.

You can also explicitly tell Requests to use it:

import requests
import certifi

response = requests.get(
"https://example.com",
verify=certifi.where(),
timeout=15
)

print(response.status_code)

If the request succeeds, the issue may have been related to the previous CA bundle configuration.

4. Check Whether Your Server Is Sending the Complete Certificate Chain

Another common problem occurs when a web server doesn't provide the required intermediate certificates.

A TLS certificate chain generally contains:

The server certificate.

One or more intermediate CA certificates.

A trusted root CA certificate, which is generally already present in the client's trust store.

If the server fails to provide a required intermediate certificate, some clients may be unable to build a trusted chain.

You can inspect the certificate chain using:

openssl s_client \
-connect example.com:443 \
-servername example.com \
-showcerts \
-verify_return_error

Check whether the expected intermediate certificates are being presented and whether chain verification succeeds.

If the chain is incomplete, correct the server's certificate-chain configuration rather than disabling verification in Python.

5. Configure a Custom CA Bundle When Necessary

Sometimes the problem isn't an outdated public CA bundle.

For example, your organization might use:

An internal Certificate Authority.

A private PKI environment.

A corporate HTTPS inspection proxy.

Custom certificates for internal APIs.

In these situations, Python may need to trust an additional CA certificate.

Requests supports specifying a custom CA bundle through the verify parameter:

import requests

response = requests.get(
"https://internal.example.com",
verify="/path/to/company-ca-bundle.pem",
timeout=15
)

print(response.status_code)

You can also configure the REQUESTS_CA_BUNDLE environment variable:

Linux/macOS:

export REQUESTS_CA_BUNDLE=/path/to/company-ca-bundle.pem

Windows PowerShell:

$env:REQUESTS_CA_BUNDLE = "C:\certs\company-ca-bundle.pem"

Make sure the file contains the appropriate trusted CA certificates and is maintained securely.

If you need both public and private CA trust, use a properly constructed combined CA bundle rather than accidentally replacing public trust with an incomplete private bundle.

6. Check for HTTPS Inspection by a Corporate Proxy

If your Python application works on your home network but fails on an office network, check whether a corporate proxy is inspecting HTTPS traffic.

Some organizations intercept encrypted connections and issue replacement certificates signed by an internal CA.

Your browser may already trust that internal CA because it was installed through your organization's device-management policy.

Python may not automatically use the same trust configuration.

To investigate:

Compare the certificate issuer on your office and home networks.

Check whether your organization uses HTTPS inspection.

Ask your IT administrator for the approved CA certificate bundle.

Configure your Python application to trust the appropriate CA.

Never install an unknown CA certificate simply to make an SSL error disappear. Verify its authenticity with your IT or security team.

7. Why You Shouldn't Disable SSL Verification

You may find examples online that suggest using:

requests.get(
"https://example.com",
verify=False
)

This disables certificate verification for that request.

Although it might make the connection work, it also removes an important security check. Your application could accept an untrusted or impersonated server certificate.

This increases the risk of man-in-the-middle attacks.

Instead of disabling verification, identify why the certificate cannot be trusted and fix the underlying problem.

A Quick Troubleshooting Checklist

If your Python application still cannot verify an SSL certificate, work through this checklist:

Check whether the server certificate has expired.

Verify that the hostname matches the certificate.

Check whether the server sends the required intermediate certificates.

Update Requests and Certifi.

Check which CA bundle your application uses.

Investigate corporate proxies or private CAs.

Test the connection again without disabling certificate verification.

Final Thoughts

Python SSL certificate verification errors aren't always caused by an invalid website certificate. Sometimes the issue is with the local CA bundle, missing intermediate certificates, or a network environment that uses its own trusted CA.

The best approach is to identify where certificate verification fails before changing your application's security settings.

If you regularly work with APIs, backend services, or automated deployments, keeping CA bundles updated and monitoring certificate chains can help prevent unexpected connection failures.

Have you encountered an SSL certificate verification error in Python? Was it caused by an outdated CA bundle, a missing intermediate certificate, or something else? Share your experience in the comments.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.