Fix SSL certificate verification errors in urllib

Read the verification reason, select the intended trusted CA set explicitly, and distinguish Python trust paths from Requests-specific configuration.

Best practices
Illustration of software components, data, and application workflows

CERTIFICATE_VERIFY_FAILED describes a class of TLS failures. Read the reason that follows it: an untrusted issuer, incomplete chain, expired certificate, or hostname mismatch needs a different repair. Keep verification enabled while identifying the cause.

Interpret the verification reason

An error such as this indicates that the client could not build the required issuer chain:

urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate>

That can reflect a missing client trust anchor or an intermediate certificate omitted by the server. It does not prove that installing a new public CA bundle is the only solution. Check the clock, exact hostname, server chain, and any inspecting proxy.

Configure a public CA bundle explicitly

If certifi is the intended public trust source, install or update it through an already trusted package channel, then pass its bundle to the context:

import certifi
import ssl
import urllib.request

context = ssl.create_default_context(cafile=certifi.where())
with urllib.request.urlopen(
    "https://example.com", context=context, timeout=10
) as response:
    data = response.read()

Installing certifi alone does not make urllib use it. Supplying cafile here selects that CA set rather than automatically combining it with every system root. Requests has its own verify bundle option; it is not configured through this urllib context.

Add an approved private CA

Obtain the root through a controlled IT/PKI channel and verify its SHA-256 fingerprint. To retain the default roots and add that CA:

import ssl

context = ssl.create_default_context()
context.load_verify_locations(cafile="/path/to/internal-root-ca.pem")

Use the resulting context in urlopen as above. Alternatively, create a context with only an explicitly chosen private bundle when that is the intended trust policy. In either case, the server name must still match its certificate.

Check operating-system installation details

For a python.org macOS installation that supplies a certificate setup script, run the script matching the installed version, for example:

open "/Applications/Python 3.12/Install Certificates.command"

It configures that installation's certificate path and may install certifi. It is not a universal macOS repair, and any network download it performs still needs a trusted route.

On Linux, update or install the distribution's ca-certificates package through its normal package manager. Debian-family systems and Alpine use different tools; minimal containers may have no usable CA package at all. Verify the running Python's paths with ssl.get_default_verify_paths() instead of assuming package installation automatically affects every Python build.

FAQ

Why does the browser succeed?

Browsers can use different trust sources, cached intermediates, and proxy configuration. Compare the actual chain and client setup; browser success is useful evidence, not proof that Python should accept the same connection.

Does REQUESTS_CA_BUNDLE control urllib?

No. That variable is a Requests convention. OpenSSL default paths can be influenced by variables such as SSL_CERT_FILE in applicable builds, but an explicit SSLContext makes this application's trust selection easier to inspect.

What if certifi is current but validation still fails?

Check for a private proxy CA, missing server intermediates, wrong hostname, expiry, or stricter validation in the installed Python/OpenSSL. A public root update cannot solve all of those.

Do not replace the context with an unverified one or disable hostname checks. Repair the actual failure, then confirm both a valid connection and rejection of an untrusted or wrongly named peer in an appropriate test environment.

References: Python SSL contexts, Python urllib requests, Using Python on macOS.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting