---
title: "urllib3 Breaking Changes on PyPI | Since.dev"
description: "What each urllib3 release removed, renamed or changed: 37 breaking changes in 6 versions on PyPI, each with its source."
canonical_url: https://since.dev/packages/pypi/urllib3
last_updated: 2026-09-15
---

# urllib3 breaking changes

Every breaking API change we have recorded in urllib3 on PyPI: 37 breaking changes in 6 versions. Below are its newest 6 versions, removals and renames first, each with its source.

Newest change September 15, 2026

## 2.8.0

14 breaking changes, first published September 15, 2026.

- **Behavior changed**  

Percent-encoded SOCKS proxy credentials are now decoded before authentication. Credentials that were sent encoded will now be sent decoded. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed**  

Explicit :0 ports are kept in URL parsing, pool selection, proxy configuration and connection_from_url() rather than replaced with 80 or 443. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed**  

Obsolete folded header lines are now replaced with spaces, so raw CRLF sequences no longer appear in header values such as Set-Cookie. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `assert_fingerprint`  

A fingerprint of supported length with non-hex characters now raises SSLError. Code catching binascii.Error must catch SSLError instead. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `ConnectionPool.pool`  

LifoQueue is now resolved from the queue module after the ConnectionPool is instantiated, so late monkey-patching such as by gevent takes effect. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `HTTPConnectionPool.urlopen`  

URL fragments are now removed from absolute request targets before sending, including through HTTP proxy forwarding. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `HTTPSConnection.connect`  

Destination client certificates and identity checks no longer apply to HTTPS forwarding proxies. Configure proxy certificates in proxy_ssl_context and use proxy_assert_hostname or proxy_assert_fingerprint for identity checks. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `is_ipaddress`  

Hex, octal and integer IPv4 forms are now detected as IP addresses, so certificate verification uses IP mode for them. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `parse_url`  

Hosts with raw spaces, control characters or malformed percent-encodings are now rejected, and percent-encoded unreserved characters are decoded during normalization. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)
- **Behavior changed** `UnrewindableBodyError`  

Retrying a request whose body supports tell() but not seek() now raises UnrewindableBodyError instead of ValueError. Code catching ValueError must catch UnrewindableBodyError. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.8.0)

And 4 more in this version.

## 2.7.0

6 breaking changes, first published May 7, 2026.

- **Removed**  

PyPy3.10 is no longer supported. Users on PyPy3.10 must upgrade their interpreter. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.7.0)
- **Removed**  

Python 3.9 is no longer supported. Users on Python 3.9 must upgrade their interpreter. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.7.0)
- **Removed** `urllib3.util.ssl_.VERIFY_X509_PARTIAL_CHAIN`  

urllib3.util.ssl\_.VERIFY_X509_PARTIAL_CHAIN was removed in urllib3 2.7.0 [pypi.org](https://pypi.org/project/urllib3/2.7.0/)
- **Behavior changed**  

Existing deprecation notices are now emitted as FutureWarning, so code that filters or expects DeprecationWarning may behave differently. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.7.0)
- **Configuration changed**  

Environments with an older pyOpenSSL must upgrade to version 19.0.0 or later. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.7.0)
- **Signature changed** `urllib3.BaseHTTPResponse.readinto`  

Was def readinto(self, b: bytearray) -> int; now def readinto(self, b: bytearray | memoryview\[int\]) -> int. [pypi.org](https://pypi.org/project/urllib3/2.7.0/)

## 2.6.3

2 breaking changes, first published January 7, 2026.

- **Behavior changed**  

Retry handling now treats Retry-After values greater than 6 hours as 6 hours by default. Code that relied on longer waits must configure its retry behavior explicitly. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.6.3)
- **Signature changed** `urllib3.Retry.__init__`  

Was def \_\_init\_\_(self, total: bool | int | None = 10, connect: int | None = None, read: int | None = None, redirect: bool | int | None = None, status: int | None = None, other: int | None = None, allowed_methods: typing.Collection\[str\] |... [pypi.org](https://pypi.org/project/urllib3/2.6.3/)

## 2.6.0

9 breaking changes, first published December 5, 2025.

- **Removed** `urllib3.BaseHTTPResponse.getheader`  

HTTPResponse.getheader(name, default) is gone. Use HTTPResponse.headers.get(name, default) instead. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)
- **Removed** `urllib3.BaseHTTPResponse.getheaders`  

HTTPResponse.getheaders() is gone. Use the HTTPResponse.headers attribute instead. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)
- **Behavior changed**  

Responses with more than 5 chained Content-Encoding values are now refused during decoding. Code that relies on longer chains must change. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.6.0)
- **Configuration changed**  

On Python 3.13 and before, the zstd extra now installs backports.zstd instead of zstandard. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.6.0)
- **Signature changed** `urllib3.connectionpool.MaxRetryError.__init__`  

Was def \_\_init\_\_(self, pool: ConnectionPool, url: str, reason: Exception | None = None) -> None; now def \_\_init\_\_(self, pool: ConnectionPool, url: str | None, reason: Exception | None = None) -> None. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)
- **Signature changed** `urllib3.exceptions.RequestError.__init__`  

Was def \_\_init\_\_(self, pool: ConnectionPool, url: str, message: str) -> None; now def \_\_init\_\_(self, pool: ConnectionPool, url: str | None, message: str) -> None. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)
- **Signature changed** `urllib3.response.BytesQueueBuffer.buffer`  

Was buffer: typing.Deque\[bytes\]; now buffer: typing.Deque\[bytes | memoryview\[bytes\]\]. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)
- **Signature changed** `urllib3.response.ContentDecoder`  

The ContentDecoder API changed, so custom decompressors must be updated to match the new interface. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.6.0)
- **Signature changed** `urllib3.response.ContentDecoder.decompress`  

Was def decompress(self, data: bytes) -> bytes; now def decompress(self, data: bytes, max_length: int = -1) -> bytes. [pypi.org](https://pypi.org/project/urllib3/2.6.0/)

## 2.5.0

3 breaking changes, first published June 18, 2025.

- **Behavior changed**  

In the Node.js runtime, redirects are now handled by urllib3 rather than the runtime. Check code that depends on the previous redirect behavior. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.5.0)
- **Behavior changed**  

When retries is passed, pool managers now control redirects themselves. Check code that relies on the previous redirect handling. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.5.0)
- **Behavior changed** `HTTPResponse.shutdown`  

Calling shutdown on a response whose connection was already released to the pool now raises an exception. Code that calls it in this state must handle the exception. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.5.0)

## 2.4.0

3 breaking changes, first published April 10, 2025.

- **Default changed** `create_urllib3_context`  

On Python 3.13 and later, create_urllib3_context now defaults to VERIFY_X509_PARTIAL_CHAIN and VERIFY_X509_STRICT. Pass the verify_flags option to override these defaults. [github.com](https://github.com/urllib3/urllib3/releases/tag/2.4.0)
- **Signature changed** `urllib3.connection.create_urllib3_context`  

Was def create_urllib3_context(ssl_version: int | None = None, cert_reqs: int | None = None, options: int | None = None, ciphers: str | None = None, ssl_minimum_version: int | None = None, ssl_maximum_version: int | None = None) ->... [pypi.org](https://pypi.org/project/urllib3/2.4.0/)
- **Signature changed** `urllib3.util.create_urllib3_context`  

Was def create_urllib3_context(ssl_version: int | None = None, cert_reqs: int | None = None, options: int | None = None, ciphers: str | None = None, ssl_minimum_version: int | None = None, ssl_maximum_version: int | None = None) ->... [pypi.org](https://pypi.org/project/urllib3/2.4.0/)

## Check your code

These are the changes. To see which of them reach your code, [scan a public repository](https://since.dev/scan.md), or have your coding agent call `check_code` with your manifest or lockfile over [MCP](https://since.dev/docs.md#mcp). [The API](https://since.dev/docs.md#api) reads every change in every version, not only the newest. On Team and up, a fix pull request opens for the changes that reach a connected repository. Nothing merges or deploys.

## Put your code on continuous management.

Scan a public repository now, or give your agent one line.

### See how out of date your code is.

[Scan a repository](https://since.dev/scan.md). Any public GitHub repository. No sign-in.

### Tell your agent what changed since it was trained.

`claude mcp add --transport http since https://api.since.dev/mcp`

---

Every page on this site as Markdown: https://since.dev/sitemap.md. For agents: https://since.dev/llms.txt.
