pypa/pip · error · URLError
Unexpected HTTP request on what should be a secure…
Error message
Unexpected HTTP request on what should be a secure connection: %s
What it means
Raised by distlib's HTTPSOnlyHandler.http_open. This handler class inherits from both HTTPSHandler and HTTPHandler so that build_opener does not register its own plain-HTTP handler; instead, every plain http:// request is intercepted and rejected. This prevents accidental HTTP traffic (and MITM downgrade attacks) on a connection intended to be HTTPS-only, e.g. when an index page contains an http:// link where an https:// link was expected.
Solutions
- Ensure all URLs in your configuration use the https:// scheme.
- If the source is an index page, fix the server to serve https:// links.
- Verify no redirect in the chain downgrades from HTTPS to HTTP.
- If plain HTTP is genuinely required for this endpoint, use HTTPSHandler instead of HTTPSOnlyHandler (accepting the security trade-off).
Example fix
# before — plain HTTP URL rejected
opener = build_opener(HTTPSOnlyHandler(...))
opener.open('http://pypi.org/simple/') # raises
# after — use HTTPS
opener.open('https://pypi.org/simple/') Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
def ensure_https(url):
parts = urlsplit(url)
if parts.scheme != 'https':
raise ValueError(f'Expected https:// URL, got {parts.scheme}:// for {url}')
return url Try / catch
from urllib.error import URLError
try:
opener.open(url)
except URLError as e:
if 'Unexpected HTTP request' in str(e):
log.error('HTTP URL used on HTTPS-only handler: %s', url)
raise Prevention
- Normalize all URLs to https:// at the application boundary.
- Validate URL scheme before passing to an HTTPSOnlyHandler opener.
- Audit index page HTML for http:// links in your infrastructure.
When it happens
Trigger: Building a URL opener with HTTPSOnlyHandler and then requesting a URL with an http:// scheme, or following a redirect/link from an HTTPS index page that points to a plain HTTP URL.
Common situations: A PyPI index serves HTML with http:// links by mistake; a redirect chain degrades from https to http; developer accidentally passes an http:// URL where https:// was intended; corporate proxy strips TLS and serves plain HTTP.
Related errors
- Unable to verify server certificate for
- Absolute paths are not supported in pylock files obtained…
- Can not open an editor for a file name containing
- Can't verify hashes for these file:// requirements because…
- Can't verify hashes for these requirements because we don't…
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/4233027a71399f37.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/distlib/util.py:1559
if 'certificate verify failed' in str(e.reason):
raise CertificateError('Unable to verify server certificate '
'for %s' % req.host)
else:
raise
#
# To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The-
# Middle proxy using HTTP listens on port 443, or an index mistakenly serves
# HTML containing a http://xyz link when it should be https://xyz),
# you can use the following handler class, which does not allow HTTP traffic.
#
# It works by inheriting from HTTPHandler - so build_opener won't add a
# handler for HTTP itself.
#
class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler):
def http_open(self, req):
raise URLError('Unexpected HTTP request on what should be a secure '
'connection: %s' % req)
#
# XML-RPC with timeouts
#
class Transport(xmlrpclib.Transport):
def __init__(self, timeout, use_datetime=0):
self.timeout = timeout
xmlrpclib.Transport.__init__(self, use_datetime)
def make_connection(self, host):
h, eh, x509 = self.get_host_info(host)
if not self._connection or host != self._connection[0]:
self._extra_headers = eh
self._connection = host, httplib.HTTPConnection(h)
return self._connection[1]View on GitHub (pinned to f399c37189)