Programming languages

InsecureRequestWarning "Unverified HTTPS request": what to do

By · Last verified

Error messageInsecureRequestWarning: Unverified HTTPS request is being made to host

InsecureRequestWarning: Unverified HTTPS request is being made to host 'example.com' means your program made an HTTPS request without checking the server's certificate. The request succeeded, but some code, usually a verify=False added to get past a certificate error, switched off verification. Find that switch, fix the certificate problem it hid, and remove it.

What the warning means

The warning means a request went out with certificate verification turned off. urllib3, the library under requests, pip, botocore and many SDKs, checks every HTTPS connection right before sending a request. If the connection was made with cert_reqs='CERT_NONE' (which is what verify=False in requests turns into) and no certificate fingerprint was pinned, it calls warnings.warn() with this text:

InsecureRequestWarning: Unverified HTTPS request is being made to host 'example.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings

Python prefixes it with the file and line that triggered it, for example .../urllib3/connectionpool.py:<line>:. That location is inside urllib3, not in your code, which is why the cause is often hard to find. The traffic is still encrypted, but without verification the encryption is with whoever answered. A proxy, a hijacked DNS record or a hostile Wi-Fi network can present any certificate and read the data.

Causes, most common first

1. Your code passes verify=False

The usual story: a request failed with CERTIFICATE_VERIFY_FAILED, someone added verify=False, and the warning replaced the error.

  • Confirm: search the code base.
grep -rnE "verify\s*=\s*False|CERT_NONE|_create_unverified_context" --include=*.py .
  • Fix: remove it and fix the original error (sections below).

2. A session, client or SDK option disables verification

session.verify = False, a client option such as ssl_verify=False, or a command-line flag like the AWS CLI's --no-verify-ssl all end up as an unverified urllib3 connection. Configuration files and environment-specific settings (a DEBUG or DEV profile) are common places to hide it.

  • Confirm: turn the warning into an exception to get a full traceback:
python -W error::urllib3.exceptions.InsecureRequestWarning app.py
  • Fix: switch the option back on and provide a CA bundle through the library's own setting.

3. A third-party library disables verification on its own

Some older SDKs and scrapers hardcode verify=False. The traceback from step 2 ends inside that package.

  • Fix: upgrade the package, or pass it a session or option with verification enabled. If it offers neither, report it to the maintainers; that is a vulnerability in the library.

4. Direct urllib3 use with CERT_NONE

Code that builds urllib3.PoolManager(cert_reqs="CERT_NONE") triggers the warning on every request. Remove the argument: urllib3 2.x verifies by default.

Fix commands by platform

Each of these gives Python a way to trust the server, so verify=False becomes unnecessary. Check the server first with the SSL checker; if its chain is incomplete, fix the server rather than the client.

Debian / Ubuntu / containers:

cp company-root.crt /usr/local/share/ca-certificates/company-root.crt
update-ca-certificates
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

RHEL family:

cp company-root.crt /etc/pki/ca-trust/source/anchors/ && update-ca-trust
export REQUESTS_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt

Windows and macOS: use the operating system trust store, including roots deployed by IT, with truststore (Python 3.10+):

import truststore
truststore.inject_into_ssl()

One internal service only:

import requests
requests.get("https://intranet.example.com", verify="/etc/ssl/certs/company-root.pem")

A lab device with a self-signed certificate you cannot change: export the certificate with openssl s_client -connect 203.0.113.10:443 -showcerts, inspect it in the certificate decoder, and either pass it as verify= or pin its fingerprint with urllib3:

import urllib3
http = urllib3.PoolManager(
    cert_reqs="CERT_NONE",
    assert_fingerprint="<sha256 fingerprint of the device certificate>",
)
http.request("GET", "https://203.0.113.10/")

With assert_fingerprint urllib3 marks the connection as verified, so no warning is printed, and a different certificate is rejected.

Don't silence the warning

urllib3.disable_warnings(), requests.packages.urllib3.disable_warnings(), warnings.filterwarnings("ignore") and PYTHONWARNINGS=ignore remove the message but not the risk. The urllib3 documentation states that making unverified HTTPS requests is strongly discouraged. Without verification, credentials, session cookies and API responses can be read or altered by anyone positioned between you and the server, and silencing the warning makes the hole invisible in logs and code review.

The safer alternatives above cover every real case: a CA bundle for private and proxy roots, truststore for operating system roots, a complete chain on the server, and pinning for a single device you cannot fix. See certificate chains explained for why the chain matters.

Verify the fix

python -W error::urllib3.exceptions.InsecureRequestWarning -c "import requests; print(requests.get('https://example.com', timeout=10).status_code)"

With warnings turned into errors, this command fails if any unverified request remains on that code path. Run your application's test suite the same way, ideally in CI, so a future verify=False breaks the build instead of shipping quietly. Finally, search the code once more for verify=False and CERT_NONE and confirm nothing is left.

FAQ

Is InsecureRequestWarning an error? My script still works.

It is a warning, and that is exactly the problem: the request worked without proving the server's identity. A machine in the middle could have answered instead of the real server and your script would not notice. Treat it as a security bug, not as noise.

How do I find which library is making the unverified request?

Turn the warning into an exception so you get a traceback: run with python -W error::urllib3.exceptions.InsecureRequestWarning app.py, or call warnings.simplefilter('error', urllib3.exceptions.InsecureRequestWarning) at startup. The traceback shows the exact call site, including calls inside third-party SDKs.

Is urllib3.disable_warnings() acceptable in tests?

In a test suite that talks to a local mock server you control, it is a judgement call. A better pattern is to give the test server a certificate from a local test CA and pass that CA file with verify=, so the tests exercise the same code path as production.

I must talk to a device with a self-signed certificate I cannot replace. What is the least bad option?

Trust that one certificate instead of turning verification off: save the device certificate as PEM and pass verify='/path/device.pem'. If the name in the certificate cannot match, urllib3 can pin the certificate's SHA-256 fingerprint with assert_fingerprint; urllib3 then treats the connection as verified and does not emit the warning.

Sources

  1. urllib3: Advanced Usage — TLS Warnings
  2. urllib3 source: connectionpool.py
  3. Requests: Advanced Usage (SSL Cert Verification)
  4. Python docs: warnings — Warning control
  5. truststore documentation