urllib3/urllib3 · error · ValueError
Tunnel host can't contain control characters %r
Error message
Tunnel host can't contain control characters %r
What it means
In the Python<3.11.16 backport of HTTPConnection._tunnel (src/urllib3/connection.py:297), urllib3 rejects a tunnel host containing control/whitespace characters (matched by _contains_disallowed_url_pchar_re = [\x00-\x20\x7f]) before sending the CONNECT request. This prevents request smuggling / header injection through a crafted proxy host. ValueError is raised at line 299.
Source
Thrown at src/urllib3/connection.py:299
if b":" in ip and ip[0] != b"["[0]:
return b"[" + ip + b"]"
return ip
# Copied from CPython 3.12.13 Lib/http/client.py
_is_legal_header_name = staticmethod(re.compile(rb"[^:\s][^:\r\n]*").fullmatch)
_is_illegal_header_value = staticmethod(
re.compile(rb"\n(?![ \t])|\r(?![ \t\n])").search
)
_contains_disallowed_url_pchar_re = re.compile("[\x00-\x20\x7f]")
if sys.version_info < (3, 11, 16):
# `_tunnel` copied from 3.11.15 backporting
# https://github.com/python/cpython/commit/0d4026432591d43185568dd31cef6a034c4b9261
# and https://github.com/python/cpython/commit/6fbc61070fda2ffb8889e77e3b24bca4249ab4d1
# plus a fix from https://github.com/python/cpython/commit/56b7100b04e44ea27989242b176beb8f016b2c53
def _tunnel(self) -> None:
if self._contains_disallowed_url_pchar_re.search(self._tunnel_host): # type: ignore[arg-type]
raise ValueError(
"Tunnel host can't contain control characters %r"
% (self._tunnel_host,)
)
_MAXLINE = http.client._MAXLINE # type: ignore[attr-defined]
connect = b"CONNECT %s:%d HTTP/1.0\r\n" % ( # type: ignore[str-format]
self._wrap_ipv6(self._tunnel_host.encode("ascii")), # type: ignore[union-attr]
self._tunnel_port,
)
headers = [connect]
for header, value in self._tunnel_headers.items(): # type: ignore[attr-defined]
header_bytes = header.encode("latin-1")
value_bytes = value.encode("latin-1")
if not self._is_legal_header_name(header_bytes):
raise ValueError(f"Invalid header name {header_bytes!r}")
if self._is_illegal_header_value(value_bytes):
raise ValueError(f"Invalid header value {value_bytes!r}")
headers.append(b"%s: %s\r\n" % (header_bytes, value_bytes))
headers.append(b"\r\n")View on GitHub (pinned to c8d039c1b7)
Solutions
- Sanitize the proxy host: strip and reject any char in \x00-\x20 or \x7f before configuring the tunnel.
- Parse proxy URLs with urllib3.util.url.parse_url and validate the host is a clean hostname/IP.
- Source proxy config from trusted, immutable config rather than raw env vars.
- Reject leading/trailing whitespace and any embedded \r\n in host values.
Example fix
# before
os.environ["HTTPS_PROXY"] = "http://evil .host:8080\r\nX-Inject: yes" # rejected at tunnel
# after
import re
_clean = re.compile(r"[\x00-\x20\x7f]")
host = _clean.sub("", proxy_host)
assert host == proxy_host, "proxy host has control chars"
conn.set_tunnel(host, port) Defensive patterns
Strategy: validation
Validate before calling
import re
_BAD_HOST = re.compile(r"[\x00-\x20\x7f]")
def clean_proxy_host(host: str) -> str:
if _BAD_HOST.search(host):
raise ValueError(f"proxy host has control chars: {host!r}")
return host Type guard
null
Try / catch
null
Prevention
- Reject any control/whitespace char in proxy host config.
- Parse proxy URLs with urllib3.util.url.parse_url and validate.
- Treat proxy env vars as untrusted; sanitize before use.
- Prefer fixed, reviewed proxy config over runtime env vars.
When it happens
Trigger: Tunneling through a proxy whose configured host contains spaces, tabs, newlines, NUL, DEL, or other control bytes; e.g. a proxy URL parsed from untrusted input, a host with a trailing CRLF, or an env var carrying injected characters. Only affects interpreters below 3.11.16 (the guard is a vendored security backport).
Common situations: ALL_PROXY/HTTPS_PROXY env vars sourced from untrusted data; config files with stray whitespace/newlines; a host string built by string concatenation that included a CRLF; test fixtures with malformed hosts.
Related errors
- Invalid header name {header_bytes!r}
- Invalid header value {value_bytes!r}
- Invalid proxy scheme for tunneling: {scheme!r}, must be eith
- Host {host!r} contains invalid character {invalid_host_char.
- Host {host_port!r} contains invalid character {invalid_host_
AI-assisted analysis of urllib3/urllib3@c8d039c1b7 (2026-08-04).
Data as JSON: /data/errors/fd4149e6c5a45ade.json.
Report an issue: GitHub.