# socks5 与 socks5h：六个客户端实际发给代理的是什么

Source: https://ipvolt.com/zh/blog/socks5-vs-socks5h
Markdown: https://ipvolt.com/zh/blog/socks5-vs-socks5h.md
Language: zh-CN

[ipvolt 首页](https://ipvolt.com/zh.md) / [博客](https://ipvolt.com/zh/blog.md) / SOCKS5 与 SOCKS5h 的区别：6 个客户端实测

对比
发布于: 2026-10-04
更新于: 2026-10-04
作者： ipvolt
阅读约 3 分钟

socks5:// 并不是在每个客户端里都表示本地 DNS。我们记录了 curl、Requests、HTTPX、aiohttp、Playwright 和 Node 在每种协议写法下发给 SOCKS5 代理的地址。

在 curl 中，`socks5://` 在你的机器上解析目标主机名，`socks5h://` 则把主机名交给代理。其他客户端并不遵循这条规则：在这次测试的六个客户端中，有三个在使用普通的 `socks5://` 时发送了主机名，其中两个在连接之前就拒绝了 `socks5h://`。

| 客户端（测试版本） | `socks5://` 发给代理的内容 | `socks5h://` |
| --- | --- | --- |
| curl 8.22.0 | IPv4 地址（本地解析） | 主机名 |
| Requests 2.34.2 + PySocks 1.7.1 | IPv4 地址（本地解析） | 主机名 |
| HTTPX 0.28.1 + socksio 1.0.0 | 主机名 | 主机名 |
| aiohttp 3.14.3 + aiohttp-socks 0.12.0 | 主机名 | `ValueError`，没有建立连接 |
| Playwright 1.63.0（Chromium 153.0.8010.12） | 主机名 | `net::ERR_NO_SUPPORTED_PROXIES`，没有建立连接 |
| Node.js 24.20.0 + socks-proxy-agent 10.1.0 | IPv4 地址（本地解析） | 主机名 |

每个单元格记录的是：让客户端请求 `http://dnsprobe.example/` 时，`127.0.0.1` 上一个带日志的 SOCKS5 服务器收到了什么。这个名称只存在于本地 hosts 文件中，指向 `127.0.0.2`。“IPv4 地址”表示代理看到的是 `127.0.0.2`，始终不知道主机名。“主机名”表示代理收到了 `dnsprobe.example`，需要由它来解析。换成 `https://` 目标时，每个客户端的结果相同。

## 为什么地址类型很重要

一个 SOCKS5 请求只带一个目标地址，它的 `ATYP` 字节说明地址类型：`X'01'` 是 IPv4，`X'03'` 是域名，`X'04'` 是 IPv6。[RFC 1928 第 4 节](https://www.rfc-editor.org/rfc/rfc1928#section-4) 发送域名的客户端把 DNS 留给代理；发送 IP 地址的客户端已经自己完成了查询。这会带来三个后果：

1. **你自己的解析器会看到这次查询。** 目标站点的 DNS 查询会发往你机器上配置的解析器，不经过代理连接。Requests 文档说得很直接：`socks5` “causes the DNS resolution to happen on the client, rather than on the proxy server”。[Requests：SOCKS](https://requests.readthedocs.io/en/latest/user/advanced/#socks)
2. **得到的答案可能属于错误的位置。** RFC 7871 指出，许多权威域名服务器 “return different responses based on the perceived topological location of the user”，依据是查询来源的地址，而这通常是你的解析器。[RFC 7871 第 1 节](https://www.rfc-editor.org/rfc/rfc7871#section-1) 因此，在本地解析的 CDN 主机名可能指向为你的网络挑选的服务器，而代理连接的正是这个地址。出口 IP 在你要求的地区，但它到达的服务器是为你所在的地区挑选的，于是内容可能是另一个地区的。这是从请求的构造方式推出的结论；回环测试没有测量任何 CDN。
3. **只有代理能解析的名称会失败。** 对一个在本地无法解析的名称，curl 8.22.0 停在 `curl: (6) Could not resolve host: dnsprobe-missing.example`，Requests 抛出包含 `[Errno -2] Name or service not known` 的 `ConnectionError`，socks-proxy-agent 则以 `getaddrinfo ENOTFOUND dnsprobe-missing.example` 失败。三者都没有向代理发送请求。使用 `socks5h://` 时，三者都把名称发给了代理。

[HTTP 与 SOCKS5 代理指南](/zh/guides/http-vs-socks5-proxies)说明了在选择代理协议时 DNS 的位置该如何考虑。本文余下的部分讲的是如何为每个客户端选对协议写法。

## 配置里该怎么写

| 客户端 | 把主机名发给代理 | 避免使用 |
| --- | --- | --- |
| curl | `socks5h://` 或 `--socks5-hostname` | `socks5://` 和 `--socks5` 会在本地解析 |
| Requests + PySocks | `socks5h://` | `socks5://` 会在本地解析 |
| HTTPX + socksio | `socks5://` 或 `socks5h://` | `socks://` 抛出 `ValueError` |
| aiohttp + aiohttp-socks | `socks5://` | `socks5h://` 抛出 `ValueError` |
| Playwright Chromium | `socks5://` | `socks5h://` 在导航时失败 |
| Node socks-proxy-agent | `socks5h://` 或 `socks://` | `socks5://` 会在本地解析 |

没有哪一个 URL 对六个客户端都正确。如果同一个 `PROXY_URL` 设置供多个工具使用，`socks5h://` 会让 aiohttp-socks 和 Playwright 出错，而 `socks5://` 会让 curl、Requests 和 socks-proxy-agent 在本地解析。请按客户端分别设置协议写法。

拒绝 `socks5h://` 的两个客户端失败的时机不同。aiohttp-socks 在 `ProxyConnector.from_url()` 中抛出异常，此时还没有打开任何套接字：

```text
ValueError: Invalid scheme component: socks5h
```

Playwright 在 `chromium.launch()` 中接受了这个代理，到第一次导航时才失败，而且没有连接代理：

```text
page.goto: net::ERR_NO_SUPPORTED_PROXIES at http://dnsprobe.example/
```

Playwright 文档给出的 `server` 选项的 SOCKS 写法是 `socks5://myproxy.com:3128`。[Playwright：proxy 选项](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-proxy) 其余的启动和上下文设置见 [Playwright 代理指南](/zh/guides/playwright-proxy-setup)。

不带版本的 `socks://` 同样不一致。Chromium 和 socks-proxy-agent 把它当作 SOCKS5 并发送了主机名。curl 以 SOCKS4 握手开头（首字节 `0x04`），只支持 SOCKS5 的服务器无法应答。Requests 抛出 `ValueError: Unable to determine SOCKS version from socks://127.0.0.1:11080`，HTTPX 抛出 `ValueError: Unknown scheme for proxy URL URL('socks://127.0.0.1:11080')`，aiohttp-socks 抛出 `ValueError: Invalid scheme component: socks`。

Python 这边有两个安装细节：Requests 需要 `requests[socks]` 附加依赖，HTTPX 需要 `httpx[socks]`。缺少它们时，Requests 会抛出 `Missing dependencies for SOCKS support`，HTTPX 会抛出提到 socksio 的 `ImportError`；[两种情况的解决方法](/zh/guides/fix-missing-dependencies-for-socks-support)也涵盖了代理 URL 来自 `ALL_PROXY` 的情形。[Python Requests 代理指南](/zh/guides/python-requests-proxy)和 [HTTPX 异步代理指南](/zh/guides/httpx-async-proxy)给出了完整的客户端代码。

这次测试是把代理 URL 显式传给每个客户端的。如果你的代理 URL 来自 `ALL_PROXY` 或 `HTTPS_PROXY`，请先确认客户端读取的是哪个变量；[代理环境变量指南](/zh/guides/proxy-environment-variables)按客户端列出了这一点，而 [NO_PROXY matching, tested](/zh/blog/no-proxy-matching-tested) 表明各客户端的绕过规则也不相同。

## 如何检查你自己的配置

想知道你的客户端发送了什么，并不需要代理账号。下载 [socks5_log.py](/downloads/socks5-vs-socks5h/socks5_log.py)，这是一个 42 行、没有依赖的 Python 脚本。它完成 SOCKS5 握手，打印每个请求的地址类型和地址，然后回复 “general SOCKS server failure”，所以不会转发任何流量。

给你的机器加一个只有 hosts 文件知道的测试名称。不要用 `localhost`：Chromium 会把发往 localhost 名称的请求直接发出，而不是经过代理。[Chromium: implicit bypass rules](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#Implicit-bypass-rules) 在 `/etc/hosts` 中加入这一行：

```text
127.0.0.2 dnsprobe.example
```

在一个终端中启动服务器，在另一个终端中为每种协议写法发送一个请求：

```sh
python3 socks5_log.py 1080
curl -x socks5://127.0.0.1:1080 http://dnsprobe.example/
curl -x socks5h://127.0.0.1:1080 http://dnsprobe.example/
```

使用 curl 8.18.0 时，服务器打印出：

```text
LISTENING 127.0.0.1:1080
CMD=1 IPv4 127.0.0.2 port=80
CMD=1 DOMAIN dnsprobe.example port=80
```

第一个请求以 IP 地址的形式到达，说明 curl 在本地解析了名称。第二个请求以名称的形式到达。两条 curl 命令都以 `curl: (97) cannot complete SOCKS5 connection to dnsprobe.example. (1)` 结束，这是服务器的拒绝，属于预期结果。把 curl 换成你自己的客户端和代理设置，读同样的两个字段。如果一行都没有出现，说明客户端拒绝了这种协议写法，或者绕过了代理。

## 除协议写法之外的远程 DNS 选项

有些客户端把同样的选择做成了一个选项，通常叫 `rdns`：

- **curl**：测试中 `--socks5-hostname host:port` 的行为与 `socks5h://` 相同，`--socks5 host:port` 与 `socks5://` 相同。[curl man page](https://curl.se/docs/manpage.html#-x)
- **Requests**：没有单独的选项。urllib3 的 SOCKS 管理器根据协议写法设置 PySocks 的 `rdns` 标志：`socks5` 为 `False`，`socks5h` 为 `True`。[urllib3 源码](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/contrib/socks.py) 直接使用 PySocks 时，`set_proxy()` 接受 `rdns`，文档中的默认值是 `True`。[PySocks README](https://github.com/Anorov/PySocks)
- **aiohttp-socks**：`ProxyConnector` 接受 `rdns`，README 中注明 “default is True for socks5”。[aiohttp-socks README](https://github.com/romis2012/aiohttp-socks/blob/v0.12.0/README.md) 测试中，`ProxyConnector.from_url("socks5://127.0.0.1:11080", rdns=False)` 发送了 `127.0.0.2`，`rdns=True` 发送了主机名。
- **HTTPX**：0.28.1 版把 `socks5` 和 `socks5h` 交给同一个 SOCKS 传输层，所以协议写法没有任何影响。[HTTPX 源码](https://github.com/encode/httpx/blob/0.28.1/httpx/_transports/default.py)
- **socks-proxy-agent**：只由协议写法决定它的 `lookup` 标志：`socks5` 和 `socks4` 在本地解析，`socks5h`、`socks4a` 和 `socks` 不在本地解析。[socks-proxy-agent 源码](https://github.com/TooTallNate/proxy-agents/blob/main/packages/socks-proxy-agent/src/index.ts)
- **Chromium**：没有选项。它的文档写明，对于 SOCKSv5，“name resolution is always done proxy side”。[Chromium 网络文档](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#SOCKSv5-proxy-scheme)

## 方法与局限

记录于 2026 年 10 月 4 日，环境为 Ubuntu 26.04（x86_64）、Python 3.14.4 和 Node.js 24.20.0。版本即第一张表中的版本，另有 urllib3 2.8.0、httpcore 1.0.9、python-socks 3.1.1 和 `socks` 包 2.8.10。curl 8.18.0 与 8.22.0 一起运行，发送的地址相同。对一个只解析到 IPv6 的名称，curl、Requests 和 socks-proxy-agent 发送的是 `IPv6` 地址，另外三个客户端发送的是主机名。

这个实验只有一个拒绝所有请求的回环服务器。它不能说明任何代理服务的情况，也不涉及带认证或 UDP 的 SOCKS5，以及 macOS、Windows、Firefox 或 WebKit。库的默认行为会变化，请检查你实际使用的版本。[实验压缩包](/downloads/socks5-vs-socks5h/socks5-vs-socks5h-lab.zip)包含服务器、六个客户端脚本、运行器和 [README](/downloads/socks5-vs-socks5h/README.md)；全部 62 个用例的原始输出在 [results.json](/downloads/socks5-vs-socks5h/results.json) 中。

## 相关内容

- [HTTP 与 SOCKS5 代理](/zh/guides/http-vs-socks5-proxies)：如何选择协议，以及 DNS 在哪里运行。
- [代理环境变量](/zh/guides/proxy-environment-variables)：每个客户端读取哪个变量。
- [NO_PROXY matching, tested](/zh/blog/no-proxy-matching-tested)：针对绕过规则的同类测试。
- [Playwright 代理设置](/zh/guides/playwright-proxy-setup)、[HTTPX 异步代理](/zh/guides/httpx-async-proxy)和 [Python Requests 代理](/zh/guides/python-requests-proxy)：完整的客户端配置。

ipvolt 正在为开发者和智能体构建代理基础设施。[加入候补名单](https://ipvolt.com/#waitlist-hero)，开放访问时只发一封邮件。

## 参考来源

- [curl man page: --proxy (socks5:// and socks5h://)](https://curl.se/docs/manpage.html#-x)
- [RFC 1928: SOCKS Protocol Version 5, section 4 (address types)](https://www.rfc-editor.org/rfc/rfc1928#section-4)
- [Requests: SOCKS proxies](https://requests.readthedocs.io/en/latest/user/advanced/#socks)
- [urllib3 2.8.0 source: contrib/socks.py (scheme to rdns)](https://github.com/urllib3/urllib3/blob/2.8.0/src/urllib3/contrib/socks.py)
- [PySocks README: set_proxy and rdns](https://github.com/Anorov/PySocks)
- [HTTPX: SOCKS proxies](https://www.python-httpx.org/advanced/proxies/#socks)
- [HTTPX 0.28.1 source: proxy schemes in _transports/default.py](https://github.com/encode/httpx/blob/0.28.1/httpx/_transports/default.py)
- [aiohttp-socks 0.12.0 README: ProxyConnector and rdns](https://github.com/romis2012/aiohttp-socks/blob/v0.12.0/README.md)
- [python-socks 3.1.1 source: parse_proxy_url schemes](https://github.com/romis2012/python-socks/blob/v3.1.1/python_socks/_helpers.py)
- [socks-proxy-agent source: parseSocksURL](https://github.com/TooTallNate/proxy-agents/blob/main/packages/socks-proxy-agent/src/index.ts)
- [Chromium network docs: SOCKSv5 proxy scheme and implicit bypass rules](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#SOCKSv5-proxy-scheme)
- [Playwright: browserType.launch proxy option](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-proxy)
- [RFC 7871: Client Subnet in DNS Queries, section 1](https://www.rfc-editor.org/rfc/rfc7871#section-1)

## 第一时间了解 ipvolt 开放体验。

顺便一提

ipvolt 仍在开发中。留下邮箱，开放体验时我们只会通知你一次。

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

[申请抢先体验](https://ipvolt.com/zh/blog/socks5-vs-socks5h#waitlist-blog-end)

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


## 相关文章

- [日本价格解析：日元、全角字符与税込/税抜标签](https://ipvolt.com/zh/blog/japanese-price-parsing.md) (分析, 2026年10月6日, 阅读约 2 分钟): 解析日本价格字段，同时不丢失币种和计税基础。运行一套经过测试的 Python 夹具，覆盖全角日元、混合价格以及刻意设计的待复核用例。
- [CONNECT tunnel failed 403：智能体为何遇到](https://ipvolt.com/zh/blog/connect-tunnel-failed-403.md) (分析, 2026年10月2日, 阅读约 3 分钟): 智能体的 curl 报出 CONNECT tunnel failed, response 403，而其他主机都正常。这是代理拒绝建立隧道。本文说明如何判断是谁拦截了它，以及该怎么修。
- [博彩赔率 API 与抓取对比：成本工作表](https://ipvolt.com/zh/blog/betting-odds-api-vs-scraping.md) (对比, 2026年9月28日, 阅读约 2 分钟): 用一份可编辑的工作表，按覆盖范围、数据新鲜度和建模采集成本比较博彩赔率 API 与经授权的网页抓取，并清楚写明全部工作量假设和计算方法。

## 相关指南

- [HTTP 与 SOCKS5 代理：按连接方式选择](https://ipvolt.com/zh/guides/http-vs-socks5-proxies.md): 对比 HTTP CONNECT 与 SOCKS5，理解 DNS 解析位置和 TLS 边界，并选择你的客户端和服务商实际支持的协议。
- [代理环境变量：HTTP_PROXY 与 NO_PROXY](https://ipvolt.com/zh/guides/proxy-environment-variables.md): 通过一个隔离的本地检查，诊断 HTTP_PROXY、HTTPS_PROXY、ALL_PROXY 和 NO_PROXY 在 curl、Python Requests 和 Node.js 中的路由差异。
- [在 Playwright 中配置代理](https://ipvolt.com/zh/guides/playwright-proxy-setup.md): 为 Playwright 浏览器上下文设置带认证的 HTTP 代理，保持测试状态隔离，并将页面导航与子资源请求的故障分开诊断。

## 关于 ipvolt

来自 ipvolt 团队的技术分析。

ipvolt 尚未开放使用。

[阅读英文原文](https://ipvolt.com/blog/socks5-vs-socks5h.md)
