NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #3300 most downloaded on PyPI
urllib3.future is a powerful HTTP 1.1, 2, and 3 client with both sync and async interfaces
Last release today
04 Oct 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 59 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
3 years old
176 releases · first in 2023
One column per quarter.
Nothing published for this version
Improved DNS over UDP reliability by retrying unanswered questions, starting after approximately 333 milliseconds with exponential backoff and jitter.
socket.gaierror, in both sync and async.TimeoutError on Python 3.11+. (#420)get_response() calls25 are no longer decoded twice. Covers sync and async managers,Added cohabitation wheels with a +isolation version suffix, distributed through the GitHub Pages isolation index with SLSA attestations. These install
+isolation version suffix, distributed throughurllib3_future and omit the .pth startup hook.zlib.Decompress.flush() error handling, in both sync and async.Retry(cache_response_body=True), plus Retry.async_get_retry_after() for asyncproxy_ssl_context now retains its own trust and certificate policy forDecodeErrornext_payload() releases the connection while waiting for data, allowingsend_payload() or ping(). Concurrent readers arewebsockets Sans-I/O engine.urllib3-future[ws-fast] and select ws+fast:// or wss+fast:// for syncwebsockets>=15.0.1,<18.ws:// and wss:// prefer wsproto when installed and otherwise use this backend.DirectStreamAccess and its async counterpart.UDP_SEGMENT after probing GSO support, preventing unintended segmentationdrain_conn() while preserving upgradedUnrewindableBodyError when a request body has no seek() method from upstreamSSLError for non-hexadecimal certificate fingerprints from upstreampython-socks and the PySocks fallback, preserving explicitUrl.auth_decoded and Url.auth_decoded_joined from upstreamUrl.auth percent-encoded.basic_auth_encoding and proxy_basic_auth_encoding options in make_headers() from upstreamFixed a race condition that could cause hangs during concurrent asyncio task teardown. ( jawah/niquests#460 )
Improved HTTP/1.1 response throughput by eliminating redundant body-buffer copies from the underlying state machine.
Support for qh3 v2. This major release of the quic state machine bring a substantial performance improvement. Up to approximately 40% higher throughpu
qh3 v2. This major release of the quic state machine bring a substantial performanceImproves performance by up to 10% in typical workloads, with the largest gains seen in concurrent connections and large response transfers.
Fixed HTTP/2 stream reset procedure raising unattended exception when the remote closed it before you (e.g. SSE extension).
Fixed rare in-place replacement failures during concurrent interpreter startup, interrupted operations, and user-site installations.
Restored the deprecated format_header_param and format_header_param_html5 compatibility functions. They remain aliases of format_multipart_header_para…
EINVAL.ZstdDecoder.flush() raising AttributeError with Python 3.14's stdlibcompression.zstd implementation. Incomplete Zstandard data is still rejected. (#404)format_header_param and format_header_param_html5 compatibilityformat_multipart_header_param until next major.urllib3.http2.inject_into_urllib3 compatibility shim.Fixed missing read_chunked method in HTTPResponse. This method is to be deprecated, kept for pure BC. Prefer stream at all cost. This method is merely…
read_chunked method in HTTPResponse. This method is to be deprecated, kept for pure BC.
Prefer stream at all cost. This method is merely a remnant of the http.client era.Added initial support for WASI. Most of our feature set is working in WASI. We only officially support componentize-py. Tested our solution against wa
ssl we don't by default, so only plain http://
will work unless you explicitly install rtls extra. With rtls you will be
able to request https://. Any code that depends on either urllib3 or urllib3-future
can instantaneously gain support for WASI.
Please read the instructions to gain specific knowledge about WASI in the
documentation for more.Added DNS caching to our custom resolvers. By default we will have a 60s conservative value for max TTL as we don't currently resolve intermediary ste
Fixed HTTPResponse.stream(amt) and HTTPResponse.read(amt) (and the async equivalents) blocking on the socket until amt bytes accumulated instead of se
HTTPResponse.stream(amt) and HTTPResponse.read(amt) (and the async equivalents) blocking on
the socket until amt bytes accumulated instead of serving data as it arrived. On live streams that send
a burst of data then go idle (e.g. Server-Sent Events), the initial payload was withheld indefinitely when
smaller than amt, and an in-process buffered remainder smaller than amt triggered a blocking socket
read instead of being served. The previous fix for (#379) only exercised a mocked backend and did not cover
real connections; this applies to all protocols (HTTP/1.1, HTTP/2 and HTTP/3). (#379)AsyncHTTPConnectionPool.get_response and AsyncPoolManager.get_response return None
(no promise left to resolve) without ever yielding control to the event loop. (#384)ctypes isn't available in interpreter (imcc).Added pool level TLS backend selection. You can dynamically use rtls or utls or ssl via programmatic kwargs ssl_backend. E.g. PoolManager(ssl_backend=
rtls or utls or ssl via programmatic kwargs ssl_backend.
E.g. PoolManager(ssl_backend="utls") to instantiate a PoolManager with BoringSSL backend. Supported values are utls, rtls, ssl
or None (default).Fixed PyPy certificate extraction within exposed ConnectionInfo. Unintentional regression caused by our anytls contrib module.
ConnectionInfo. Unintentional regression caused by our anytls
contrib module.Fixed http3 assert_hostname not forwarded when passing a custom SSLContext that disable it in async.
assert_hostname not forwarded when passing a custom SSLContext that disable it in async.ssl is monkeypatched by a 3rd party library.Fixed HTTPResponse.stream(amt) (and the async equivalent) no longer yielding per-frame for streamed responses.
HTTPResponse.stream(amt) (and the async equivalent) no longer yielding per-frame for
streamed responses. (#379)utls alternative TLS backend in addition to rtls.
utls is based on BoringSSL and have the capability to align with Google Chrome browser capabilities.
This new TLS backend is introduced in addition to rtls. urllib3-future tries backend in given order:
rtls -> utls -> ssl. You may override this by setting URLLIB3_FUTURE_SSL_BACKEND environment
variable. See the documentation to learn more. You can also pass a custom SSLContext from any
of those backends. We can handle multiple TLS backend at the same time within a single PoolManager instance.qh3 MTU discovery probe when the physical NIC can't handle
specific >1200 bytes datagrams. (#377)truststore 3rd party library direct alternative to wassima.
We still recommend wassima as the "OS truststore" library. It is fixed for strict BC
promise we made by being a "urllib3" inplace replacement.Fixed certs retrieval when using HTTPS proxy (Python 3.10+) in a synchronous context.
Fixed TLS connection to some server that requires OP_NO_RENEGOTIATION enabled (legacy TLS1.2 server).
Fixed performance and reliability issue when using blind multiplexing promise resolution at scale. (#369) Issuing many request using multiplexed=True
multiplexed=True and attempting to resolve them unordered (blind get_response)
could lead to significant performance downside itself leading to possible hangs (async or sync).Fixed PoolManager.get_response (and AsyncPoolManager.get_response) crashing with AssertionError when a (manual) multiplexed request reached the retry-
PoolManager.get_response (and AsyncPoolManager.get_response) crashing with AssertionError when a
(manual) multiplexed request reached the retry-on-status branch (e.g. Retry(status_forcelist=[503])). The branch
erroneously looked up a redirect Location header and assert isinstance(redirect_location, str) failed.Fixed an infinite loop in HTTPResponse.stream(amt=-1) (and AsyncHTTPResponse.stream(amt=-1)) when iterating a compressed (gzip / deflate / brotli / zs
HTTPResponse.stream(amt=-1) (and AsyncHTTPResponse.stream(amt=-1)) when iterating a
compressed (gzip / deflate / brotli / zstd) response body. Regression introduced by the decompression-bomb safeguards
backport: the decoder's internal unconsumed tail was never drained on the negative amt path, so the stream loop
kept looping forever once any tail bytes remained. The read(amt=-1) fast path now drains the decoder before
issuing another raw read, with the decoded chunk bounded by a reasonable growth factor of the most recent raw read so
the original bomb safeguard is preserved. (#364)SO_KEEPALIVE when remote only support HTTP/1.Fixed webextensions (e.g. sse/websocket) not forwarded in retries.
Fixed a rare TOCTOU race in the synchronous happy eyeballs algorithm leading to an error while attempting to close/clean challenger connections.
Fixed remote peer abrupt termination of a HTTP/3 over QUIC connection handling and exception raised.
qh3>=1.8 is installed (benefit HTTP/3 only).HTTPResponse.stream() (and AsyncHTTPResponse.stream()) to handle amt=0 instead of falling into an infinite loop. (https://github.com/urllib3/urllib3/issues/3793)HTTPResponse.read() (and AsyncHTTPResponse.read()) could cache only part of the response after a partial read when cache_content=True. (https://github.com/urllib3/urllib3/pull/4967)HTTPResponse.drain_conn() (and async equivalent) no longer triggers decompression of the remaining
body when partial decoding has already started; (2) HTTPResponse.read(amt=N) / stream(amt=N) (and async equivalents) now propagate a max_length argument to all content decoders
(Deflate, Gzip, Brotli, Zstd, Multi), preventing a single small read from triggering full decompression of a maliciously crafted response. Brotli decoders require Brotli >= 1.2.0 (or brotlicffi >= 1.2.0.0) f
or the cap to be honoured; older versions emit a DependencyWarning. (GHSA-mf9v-mfxr-j63j)Fixed async SSE race condition with close procedure.
Changed error message when asyncio TLS handshake silently fails. We now differentiate TLS-in-TLS failure from regular TLS failure.
Fixed exception raised when asyncio TLS-in-TLS (start_tls) silently fail under Python < 3.11
Fixed support for custom CPython build against FIPS OpenSSL.
ProtocolError("Remote end closed connection without response"). (https://github.com/jawah/niquests/issues/380)Changed python-socks upper bound to 2.8.1
Fixed a rare racing condition preventing the synchronous happy eyeballs algorithm to complete if all IP addresses are unreachable.
Fixed incomplete/partial background cleanup of expired connection.
Fixed missing proper exception for unknown hosts with customer resolvers.
Added support to convert stdlib ssl.SSLContext to rtls.SSLContext with best effort strategy when ctx passed directly.
Fixed race condition where asyncio transport is closed and freed (via asyncio internals) before our own procedure.
Reverted user-supplied headers now take precedence over extension defaults.
next_payload() to correctly reassemble fragmented messages by buffering intermediate frames until message_finished is received.Accept header as a compromise to the revert of #333.Fixed needless strong reference to AsyncConnectionPool instance within our background idle watcher.
Accept: text/event-stream) overwriting user-provided headers with the same key. User-supplied headers now take precedence over extension defaults. (#333)Fixed GSO capability detection in Linux.
Added support for Rustls alternative ssl backend via pip install urllib3-future[rtls]. Once the extra is installed it will be automatically picked up
pip install urllib3-future[rtls].
Once the extra is installed it will be automatically picked up for usage. Using a memory-safe
TLS backend is highly recommended for critical infrastructure project. It is completely
transparent to our software, you won't need to tune or configure it manually. Once installed
it runs automatically.rtls extra as the stdlib ssl is completely
unable to deliver such feature. The ConnectionInfo have a new member, namely tls_ech_accepted
for observability purpose on that feature.DEFAULT_KEEPALIVE_DELAY to 10min instead of 60min. We received many observations were users
saw that servers tend to silently quit the socket without properly closing it.Host header when using http proxy. (https://github.com/jawah/niquests/issues/355)Fixed http3 GRO support and some timings concerns.
Added initial support for ECH when negotiating HTTP/3 over QUIC. qh3 introduced support for ECH since version 1.7.0 and requires passing ech_config_li
ech_config_list which we now do automatically when
using a custom resolver. E.g. PoolManager(resolver="doh+cloudflare://").urlopen("GET", "https://encryptedsni.com")
will be automatically using ECH, therefore masking your intent to visit https://encryptedsni.com.Fixed segfault when leveraging in memory certificate against build of Python using statically linked ssl lib.
Automatically dispose of expired connection within the discrete idle watcher running in background.
Relax strict assertion on response object. Ease the support of vcrpy mocking utility. (#320) This alone does not solve the compatibility issue with vc
Added GRO/GSO support for Linux users in order to leverage UDP coalescing when available. This should increase QUIC/UDP performances in high throughpu
AsyncPoliceTraffic scheduler implementation in order to not penalize fast servers.Rewritten most of our weak AsyncPoliceTraffic implementation toward the direction we took with the sync counterpart. This is a impactful patch, hence
AsyncPoliceTraffic implementation toward the direction we took with the sync counterpart.
This is a impactful patch, hence the version bump. Most people should expect a performance bump when using HTTP/2+
in real world usage. While it's still imperfect we are confident this version will outperform our previous scheduler.Improved AsyncTrafficPolice for better multiplexing distribution. We recently issued a fix in 2.15.902 regarding (#309) and accidentally induced a mas
Fixed multiplexing mixing issue under specific concurrency condition.
Extended pre-check for socket liveness probe capabilities and resiliency across OSes.
Improved pre-check for socket liveness probe before connection reuse from pool.
BytesQueueBuffer class." from upstream https://github.com/urllib3/urllib3/pull/3711Content-Encoding header" from upstream https://github.com/urllib3/urllib3/commit/24d7b67eac89f94e11003424bcf0d8f7b72222a8Fixed the close procedure for webextensions when using HTTP/2 transport.
Fixed usage of asyncio.SelectEventLoop on Windows when the default proactor event loop is left aside.
asyncio.SelectEventLoop on Windows when the default proactor event loop is left aside.Fixed performance scaling issue on the free threaded build when using one or several multiplexed connection.
MustRedialError exception in DNS-over-HTTPS for rare edge cases.Fixed error when passing a BufferProtocol compatible object as a HTTP request body.
Fixed a small performance issue with a non-multiplexed connection (in both sync and async) under concurrent loads.
Fixed an async performance issue under high task concurrency pressure. The best-effort multiplexing strategy has been significantly improved. Expect b
Fixed rare edge case where a server would close the socket after executing a request, thus misleading our implementation to retry.
qh3 dependency definition constraint to include the RISCV64 platform by default.Fixed support for PySocks socks legacy proxy connector (#271).
PySocks socks legacy proxy connector (#271).contrib.socks inviting to use python-socks instead of PySocks.Fixed the determinism for the presence of this package. urllib3-future automatically takes precedence over upstream urllib3 no matter the order of ins
urllib3-future automatically takes precedence over
upstream urllib3 no matter the order of installation. This also fix the issue where a package manager would
concurrently and blindly install both. Installing your dependencies like URLLIB3_NO_OVERRIDE=true pip install niquests --no-binary urllib3-future
will not trigger the post-install procedure (ie. urllib3-future automatically takes precedence).
This will bring higher confidence of reproducibility, especially when the project relies on a lock file.Your coding agent can read these notes before it upgrades. Set up the MCP server →