# aiohttp 3.14 代理配置：认证、trust_env 与报错

Source: https://ipvolt.com/zh/guides/aiohttp-proxy-setup
Markdown: https://ipvolt.com/zh/guides/aiohttp-proxy-setup.md
Language: zh-CN

[ipvolt 首页](https://ipvolt.com/zh.md) / [指南](https://ipvolt.com/zh/guides.md) / aiohttp 3.14 代理配置：认证、trust_env 与报错

集成
发布于: 2026-10-06
阅读约 7 分钟
作者： ipvolt

在 aiohttp 3.14 中配置代理：替换已弃用的 BasicAuth 和 proxy_auth，启用 trust_env，并看懂 407 与 Bad status line 报错。基于 3.14.4 实测。

aiohttp 3.14.0（2026 年 6 月 1 日）弃用了 `BasicAuth` 和 `proxy_auth` 参数（[更新日志](https://docs.aiohttp.org/en/stable/changes.html)，[#12499](https://github.com/aio-libs/aiohttp/pull/12499)）。替代做法是用 `aiohttp.encode_basic_auth()` 生成 `Proxy-Authorization` 请求头：

```python
import asyncio

import aiohttp

PROXY = "http://proxy.example.com:8080"
PROXY_HEADERS = {"Proxy-Authorization": aiohttp.encode_basic_auth("user", "pass")}


async def main() -> None:
    async with aiohttp.ClientSession(proxy=PROXY) as session:
        async with session.get("https://example.com/", proxy_headers=PROXY_HEADERS) as response:
            print(response.status)


asyncio.run(main())
```

在我们的实验环境里，这种写法让每个 `https://` 请求都通过了认证，并且没有 `DeprecationWarning`。它没有让普通的 `http://` 请求通过认证：aiohttp 3.14.4 只在建立 HTTPS 隧道的 `CONNECT` 请求里发送 `proxy_headers`，所以对 `http://` 的 URL，代理没有收到凭据，返回了 407。

| 凭据的传递方式 | `https://` URL | `http://` URL | 弃用警告 |
| --- | --- | --- | --- |
| `proxy_headers={"Proxy-Authorization": aiohttp.encode_basic_auth(...)}` | 200 | 407 | 无 |
| `proxy="http://user:pass@host:port"` | 200 | 200 | 无 |
| `proxy_auth=aiohttp.BasicAuth(...)` | 200 | 200 | 每次调用两条 |

这些结果记录于 2026 年 10 月 6 日，使用 aiohttp 3.14.4 和 aiohttp-socks 0.12.0，运行在 Python 3.14.4 上，以 `python -W always` 启动，对着一个要求 Basic 认证的本地代理。aiohttp 3.14.0 和 3.14.3 的结果相同。

## 从 proxy_auth=aiohttp.BasicAuth 迁移

旧的调用在 3.14.4 中对两种 URL 协议仍然有效：

```python
async with session.get(url, proxy=PROXY, proxy_auth=aiohttp.BasicAuth("user", "pass")) as response:
    print(response.status)
```

每次调用打印两条警告：

```text
DeprecationWarning: BasicAuth is deprecated and will be removed in aiohttp 4.0; use aiohttp.encode_basic_auth() with headers={'Authorization': ...} instead
DeprecationWarning: The 'proxy_auth' parameter is deprecated and will be removed in v4; pass proxy_headers={'Proxy-Authorization': aiohttp.encode_basic_auth(login, password)} instead
```

替代写法：

```python
proxy_headers = {"Proxy-Authorization": aiohttp.encode_basic_auth("user", "pass")}
async with session.get(url, proxy=PROXY, proxy_headers=proxy_headers) as response:
    print(response.status)
```

上线前检查四件事：

- **普通 `http://` URL 会丢掉凭据。** [文档](https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support)里的示例把 `proxy_headers` 和 `http://` 的 URL 一起使用。在实验中，这个请求到达代理时没有 `Proxy-Authorization`，也没有我们加在 `proxy_headers` 里的自定义请求头，返回的是 407。[Issue #4422](https://github.com/aio-libs/aiohttp/issues/4422) 针对 aiohttp 3.6.2 报告过同样的现象。如果你要请求 `http://` 的 URL，把凭据写进代理 URL：`proxy="http://user:pass@host:port"` 对两种协议都返回 200，且没有警告。
- **`ClientSession` 没有 `proxy_headers` 参数。** 文档里有 `ClientSession(proxy=..., proxy_headers=...)` 的写法。在 3.14.4 中它抛出了 `TypeError: ClientSession.__init__() got an unexpected keyword argument 'proxy_headers'`。在会话上设置 `proxy=`，每次请求再传 `proxy_headers=`，就像第一个示例那样。
- **不要把这个请求头挪到 `headers=`。** 对 `https://` 的 URL，请求头在隧道内部传输。把 `Proxy-Authorization` 放进 `headers=` 后，实验中的目标服务器收到了代理凭据。
- **默认编码变了。** `encode_basic_auth()` 使用 UTF-8，而 `BasicAuth` 使用 latin1。对密码 `päss`，两者生成的请求头值不同，所以如果你的代理需要旧的字节，请传入 `encoding="latin1"`。

`encode_basic_auth()` 是 3.14 新增的（[参考文档](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.encode_basic_auth)）。在 aiohttp 3.13.5 上，同样的代码抛出了 `AttributeError: module aiohttp has no attribute encode_basic_auth`。

## 环境变量需要 trust_env=True

默认的 `ClientSession()` 会忽略代理环境变量。设置了 `HTTP_PROXY` 时，请求直接发往目标，代理什么也没有记录。下面这个会话使用了代理：

```python
async with aiohttp.ClientSession(trust_env=True) as session:
    async with session.get("http://example.com/") as response:
        print(response.status)
```

[文档](https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support)说明，aiohttp 通过 `urllib.request.getproxies()` 读取这些变量，把它们用于 HTTP、HTTPS、WS 和 WSS 协议，让 `no_proxy` 中的主机绕过代理，并且可以从 `~/.netrc` 读取代理凭据。在实验中，使用 `trust_env=True` 时：

- `HTTP_PROXY` 和小写的 `http_proxy` 对 `http://` 的 URL 生效，`HTTPS_PROXY` 对 `https://` 的 URL 生效。只设置 `HTTP_PROXY` 时，`https://` 请求是直连的。
- `ALL_PROXY` 被忽略。
- 变量的 URL 里的凭据让两种协议都通过了认证，并且没有打印警告。
- `NO_PROXY=127.0.0.1` 让请求直连。它对显式的 `proxy=` 参数不起作用，而且显式参数的优先级高于变量。
- 值为 `https://` 代理 URL 的变量被跳过。aiohttp 记录了 `HTTPS proxies https://127.0.0.1:8888 are not supported, ignoring`，然后直接连接。

[代理环境变量](/zh/guides/proxy-environment-variables)比较了这些规则在其他客户端中的情况。

## https:// URL 上的 ClientHttpProxyError: 407

当代理拒绝 `CONNECT` 请求时，aiohttp 在任何内容到达目标之前就抛出异常。代理 URL 里密码错误时：

```text
aiohttp.client_exceptions.ClientHttpProxyError: 407, message='Proxy Authentication Required', url='http://user:wrong@127.0.0.1:8888'
```

密码就在消息里，所以异常去哪里，它就去哪里：日志、调用栈、错误追踪系统。当凭据放在 `proxy_headers` 里，或者取自 `HTTPS_PROXY` 时，同样的失败打印的是 `url='http://127.0.0.1:8888'`。

对 `https://` 流量，不要把凭据放在代理 URL 里。在必须写进 URL 的地方，记录 `exc.status` 和 `exc.message`（实验中是 `407` 和 `Proxy Authentication Required`），而不是异常文本。当凭据看起来没错时，[修复代理错误 407](/zh/guides/fix-proxy-error-407) 给出了检查顺序。

## http:// URL 上状态码 407 但没有异常

对 `http://` 的 URL，代理的 407 是一个普通响应。不会抛出异常，`response.status` 是 407，`Proxy-Authenticate` 响应头里是认证质询。只捕获异常的代码会把它当成成功。

检查 `response.status`，或者用 `raise_for_status=True` 创建会话。这样抛出了：

```text
aiohttp.client_exceptions.ClientResponseError: 407, message='Proxy Authentication Required', url='http://127.0.0.1:8080/'
```

在请求上使用 `raise_for_status=True` 和调用 `response.raise_for_status()` 抛出的是同一个错误。这里的类是 `ClientResponseError`，URL 是目标的 URL，不是代理的。`ClientHttpProxyError` 是它的子类，所以 `except aiohttp.ClientResponseError` 加上对 `exc.status == 407` 的判断可以覆盖两种协议。

## socks5:// 代理 URL 导致的 Bad status line: b'\x05\x00'

aiohttp 不会拒绝 `proxy="socks5://..."`。它向 SOCKS 端口发送一个 HTTP 请求，然后在读取响应时失败：

```text
aiohttp.client_exceptions.ClientResponseError: 400, message="Bad status line:\n  Expected HTTP/, RTSP/ or ICE/:\n\n  b'\\x05\\x00'\n    ^", url='http://127.0.0.1:8080/'
```

`\x05\x00` 是 SOCKS5 服务器响应的开头，被当成了 HTTP 状态行来读取。`socks5h://` 以同样的方式失败。解决办法是使用 SOCKS 连接器，见下文。

## https:// 代理 URL 导致的 ClientConnectorSSLError

`https://` 的代理 URL 让 aiohttp 与代理本身建立 TLS。对着实验中只说明文 HTTP 的代理，请求在握手阶段失败：

```text
aiohttp.client_exceptions.ClientConnectorSSLError: Cannot connect to host 127.0.0.1:8888 ssl:default [[SSL: RECORD_LAYER_FAILURE] record layer failure (_ssl.c:1081)]
```

代理看到的是 TLS ClientHello，并用明文的 400 作了回应。代理 URL 的协议描述的是到代理的连接，而不是到目标的连接：`http://` 的代理 URL 通过 `CONNECT` 承载 `https://` 请求。这段错误文本来自 OpenSSL 3.5.5，在其他版本上可能不同。

## DeprecationWarning: BasicAuth is deprecated

只要构造 `aiohttp.BasicAuth(...)` 就会触发第一条警告，此时还没有发出任何请求。用于网站凭据的 `auth=` 参数也被弃用了。它的警告给出的替代写法是 `headers={'Authorization': aiohttp.encode_basic_auth(login, password)}`。

你可能两条都看不到。默认情况下，只有当[触发它的代码位于 `__main__`](https://docs.python.org/3/library/warnings.html#default-warning-filter) 时，Python 才显示 `DeprecationWarning`。在实验中，被弃用的调用位于被运行的脚本里时打印了警告，位于被导入的模块里时什么也没打印。`python -W always` 在两处都打印了警告。`python -W error` 把第一条变成了异常，这样跑一遍测试就能找出每个调用点。

## SOCKS 代理：使用 aiohttp-socks

```sh
python -m pip install aiohttp-socks
```

```python
import aiohttp
from aiohttp_socks import ProxyConnector

connector = ProxyConnector.from_url("socks5://127.0.0.1:1080")
async with aiohttp.ClientSession(connector=connector) as session:
    async with session.get("https://example.com/") as response:
        print(response.status)
```

使用 [aiohttp-socks](https://pypi.org/project/aiohttp-socks/) 0.12.0 时，这段代码对 `http://` 和 `https://` 的 URL 都返回 200，SOCKS 服务器记录了每个连接。两个细节：

- `socks5://` 已经把主机名发给代理。对 `http://localhost:8080/`，服务器记录的是域名。使用 `rdns=False` 时，它记录的是 `127.0.0.1`。
- `ProxyConnector.from_url("socks5h://...")` 抛出了 `ValueError: Invalid scheme component: socks5h`。[SOCKS5 与 SOCKS5h 的区别](/zh/blog/socks5-vs-socks5h)展示了各个客户端如何处理这两种协议。

Requests 和 HTTPX 需要各自的 SOCKS 依赖包，详见[解决 Missing dependencies for SOCKS support](/zh/guides/fix-missing-dependencies-for-socks-support)。

## 代理出错后的重试

407、SOCKS 的状态行错误和上面的 TLS 错误都是配置错误。同一个请求会以同样的方式再次失败，所以应该修正配置，而不是重试。对超时和断开的连接，只重试可以安全重复的请求。`POST` 可能在代理连接失败之前就已经到达目标。[代理重试：丢失一个响应，创建了两个任务](/zh/blog/proxy-retries-duplicate-jobs)展示了这种情况，以及幂等键能改变什么。

如果你用 HTTPX 做异步请求，请看 [HTTPX 异步代理](/zh/guides/httpx-async-proxy)。如果想在编码代理中获得诊断帮助，[Proxy Toolkit MCP](/mcp) 接受经过清理的错误描述。不要把凭据放进它的参数里。

## 测试方法

在 Ubuntu 26.04.1 上，使用 Python 3.14.4 和 OpenSSL 3.5.5，一切都在 `127.0.0.1` 上：`python -m http.server` 作为 HTTP 和 HTTPS 目标，一个要求 Basic 认证并记录每个请求及其请求头名称的代理，一个记录每个目的地的 SOCKS5 服务器，以及一个报告自己收到哪些请求头的 HTTPS 目标。67 个用例中的每一个都在新的解释器和干净的环境中运行。没有使用任何代理账号。

[实验归档](https://ipvolt.com/downloads/aiohttp-proxy-setup/aiohttp-proxy-lab.zip)包含[运行脚本](https://ipvolt.com/downloads/aiohttp-proxy-setup/run_lab.py)、[用例](https://ipvolt.com/downloads/aiohttp-proxy-setup/cases.py)、[代理](https://ipvolt.com/downloads/aiohttp-proxy-setup/auth_proxy.py)、[SOCKS5 服务器](https://ipvolt.com/downloads/aiohttp-proxy-setup/socks5_server.py)、[README](https://ipvolt.com/downloads/aiohttp-proxy-setup/README.md) 和[记录的结果](https://ipvolt.com/downloads/aiohttp-proxy-setup/results.json)。

未测试：macOS 和 Windows、Digest 和 NTLM 代理认证、需要认证的 SOCKS 代理、WebSocket 请求、`~/.netrc` 中的凭据，以及需要通过 TLS 连接的代理。

ipvolt 是一项面向开发者的代理服务，目前尚未开放；[加入早期访问名单](https://ipvolt.com/#waitlist-closing)，开放时你会收到一封邮件。

## 参考来源与延伸阅读

- [aiohttp changelog: 3.14.0](https://docs.aiohttp.org/en/stable/changes.html)
- [aiohttp pull request #12499: Deprecate auth. Add encode_basic_auth](https://github.com/aio-libs/aiohttp/pull/12499)
- [aiohttp Advanced Client Usage: proxy support and trust_env](https://docs.aiohttp.org/en/stable/client_advanced.html#proxy-support)
- [aiohttp Client Reference: encode_basic_auth and proxy_headers](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.encode_basic_auth)
- [aiohttp issue #4422: proxy_headers not passed to proxy](https://github.com/aio-libs/aiohttp/issues/4422)
- [aiohttp-socks on PyPI](https://pypi.org/project/aiohttp-socks/)
- [Python warnings: the default warning filter](https://docs.python.org/3/library/warnings.html#default-warning-filter)

## 相关指南

- [不靠猜测修复代理错误 407](https://ipvolt.com/zh/guides/fix-proxy-error-407.md)
- [代理环境变量：HTTP_PROXY 与 NO_PROXY](https://ipvolt.com/zh/guides/proxy-environment-variables.md)
- [HTTPX 异步代理：配置与 PoolTimeout 诊断](https://ipvolt.com/zh/guides/httpx-async-proxy.md)

## 关于 ipvolt

示例使用通用的代理设置，并附上原始技术文档链接。具体产品的行为请向你的服务商确认。ipvolt 仍在开发中。

[阅读英文原文](https://ipvolt.com/guides/aiohttp-proxy-setup.md)

## 了解何时开放体验。

ipvolt · 开发中

我们正在为开发者和数据团队打造代理基础设施。加入意向名单，ipvolt 就绪时第一时间收到通知。

开放体验时仅发一封通知邮件，不发送其他邮件。

[申请抢先体验](https://ipvolt.com/zh/guides/aiohttp-proxy-setup#waitlist-closing)

[隐私政策](https://ipvolt.com/privacy)

