httpx ConnectError: SSL CERTIFICATE_VERIFY_FAILED fix (tested)
httpx checks certificates against certifi's bundle and raises httpx.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] for a private or self-signed CA. Pass verify=ssl.create_default_context(cafile="ca.pem"), or set SSL_CERT_FILE=ca.pem.
httpx reports a TLS verification failure as httpx.ConnectError, not as an SSLError. Measured with httpx 0.28.1 on Python 3.13.5 against a local HTTPS server using a self-signed certificate (cert.pem, made with openssl req -x509); the same fix applies to a corporate proxy or private CA.
1. The error
import httpx
from srv import start
base = start() # HTTPS server with a self-signed certificate (cert.pem)
try:
httpx.get(base)
except httpx.ConnectError as e:
print(type(e).__name__ + ":", e)
Output:
ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1029)
httpx verifies against the certifi CA bundle by default, not the operating system store, so a certificate your browser or OS trusts (for example a company root CA installed system-wide) can still fail in httpx.
2. Fix: pass an SSLContext that trusts your CA
import httpx, ssl
from srv import start
base = start()
ctx = ssl.create_default_context(cafile="cert.pem") # your CA / corporate root
print(httpx.get(base, verify=ctx).status_code)
with httpx.Client(verify=ctx) as client:
print(client.get(base).text)
Output:
200
ok
verify= accepts an ssl.SSLContext, per request or on the Client / AsyncClient. Point cafile at the CA certificate (PEM) that signed the server certificate, or at the server certificate itself when it is self-signed.
3. verify="path.pem" still works but is deprecated
import httpx, warnings
from srv import start
base = start()
warnings.simplefilter("always")
r = httpx.get(base, verify="cert.pem") # string path: still works in 0.28, but deprecated
print(r.status_code)
Output:
/tmp/fx384/ve/lib/python3.13/site-packages/httpx/_config.py:51: DeprecationWarning: `verify=<str>` is deprecated. Use `verify=ssl.create_default_context(cafile=...)` or `verify=ssl.create_default_context(capath=...)` instead.
warnings.warn(message, DeprecationWarning)
200
In httpx 0.28 a string path is accepted with a DeprecationWarning pointing to ssl.create_default_context(cafile=...); switch to the SSLContext form before it is removed.
4. Without code changes: SSL_CERT_FILE
import os, httpx, subprocess, sys
from srv import start
base = start()
code = f"import httpx; print(httpx.get({base!r}).status_code)"
env = dict(os.environ, SSL_CERT_FILE="cert.pem")
out = subprocess.run([sys.executable, "-c", code], env=env, capture_output=True, text=True)
print("SSL_CERT_FILE=cert.pem ->", out.stdout.strip() or out.stderr.strip().splitlines()[-1])
Output:
SSL_CERT_FILE=cert.pem -> 200
With the default trust_env=True, httpx uses SSL_CERT_FILE (or SSL_CERT_DIR) instead of certifi when it is set. Useful when the request is made inside a library you don't control. The file replaces the bundle, so it must contain every CA you need.
5. verify=False (testing only)
import httpx
from srv import start
base = start()
r = httpx.get(base, verify=False) # no verification at all: testing only
print(r.status_code)
Output:
200
This disables certificate checking completely, so anyone in the middle can read and change the traffic. Use it only against a local test server.
Related: Python SSL CERTIFICATE_VERIFY_FAILED, self-signed certificate in certificate chain, httpx timeout, httpx follow redirects.
Frequently asked questions
Why does httpx raise ConnectError for an SSL problem?
httpx wraps TLS handshake failures in httpx.ConnectError; the message shows [SSL: CERTIFICATE_VERIFY_FAILED] and the reason, such as self-signed certificate.
Does httpx use the system certificate store?
No. By default it uses the certifi bundle, unless SSL_CERT_FILE or SSL_CERT_DIR is set (with trust_env=True, the default).
Is verify='/path/ca.pem' still supported in httpx?
In httpx 0.28.1 it works but emits a DeprecationWarning; use verify=ssl.create_default_context(cafile=...) instead.
Written with AI assistance. Measured on 2026-10-23 with httpx 0.28.1 on Python 3.13.5 against a local HTTPS http.server with a self-signed certificate. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; httpx is free.