# TLS 指纹识别 (JA3/JA4)：为什么干净的 IP 仍然被封锁

您的住宅 IP 是干净的，您的请求头完美无缺，但在第一次请求时仍然收到 403。问题出在您的 TLS 握手——这是反机器人系统在任何 HTTP 交换之前读取的指纹。

这是每个抓取者最终都会遇到的难题：住宅 IP 是干净的，User-Agent 是当前的 Chrome 字符串，请求头完美无缺——而第一次请求就返回 `403`。无论如何轮换都无法解决。原因是封锁发生在您的请求头被读取之前。现代反机器人系统会对您的 **TLS 握手**进行指纹识别，而默认的 Python、Go 或 curl 客户端在 ClientHello 中就会自我暴露为机器人，这比 HTTP 低一层。这就是 JA3 和 JA4 指纹识别的作用，以及为什么 [curl 在相同的 URL 上得到 403 而浏览器得到 200](https://quantumproxies.io/blog/curl-403-but-browser-works)。

## TLS 指纹识别读取的内容

每个 HTTPS 连接都以 TLS 握手开始。客户端发送一个 `ClientHello`，按照特定顺序声明它想要的 TLS 版本、支持的密码套件、提供的扩展（SNI、ALPN 等）、椭圆曲线和点格式。这些都不会直接命名您的浏览器——但确切的组合和顺序几乎是软件栈的唯一签名。Chrome 的列表与 Firefox 的不同，二者与 Python 的 `requests` 或 Go 的 `net/http` 差异很大。读取握手，您可以在交换任何 HTTP 字节之前猜测客户端。

## JA3：五个字段合成一个 MD5 哈希

JA3 由 Salesforce 的工程师于 2017 年发布，是经典的方法。它从 ClientHello 中提取五个字段——TLS 版本、密码、扩展、椭圆曲线和点格式——将它们连接起来，并通过 MD5 运行字符串。一个具体的例子：这些字段

```text
771,4865-4866-4867,0-11-10-35-16-5-13,29-23-24,0
```

哈希为 `e7d705a3286e19ea42f587b344ee6865`——这是标准 curl 构建的指纹。反机器人供应商会保留这些哈希的数据库。JA3 匹配已知抓取库的请求会受到更严格的速率限制、挑战或直接封锁。最强大的检查是关联：如果您的 User-Agent 声称是 Chrome 120，但您的 JA3 表示 `python-requests`，仅这一矛盾就足以让您失败。

## 为什么 JA3 被 JA4 取代

JA3 有一个弱点，使其作为稳定信号失效。从 Chrome 110 和 Firefox 114 开始，浏览器在每次连接时随机化 TLS 扩展的顺序以抵抗指纹识别。这意味着现在真实的浏览器每个会话都会生成一个*不同的* JA3 哈希——因此原始 JA3 在真实用户上会产生误报。行业的回应是 **JA3N**（一种在哈希前对扩展进行排序以取消随机化的标准化变体）和 **JA4**，这是 FoxIO 提出的由 JA3 的创建者构建的新方案。

JA4 更易读且更难伪造。它使用三部分 `a_b_c` 布局，例如 `t13d1516h2_8daaf6152771_e5627efa2ab1`。仅前缀就透露了很多信息：`t` 代表 TCP，`13` 代表 TLS 1.3，`d` 代表基于域名的 SNI，`15` 代表密码套件，`16` 代表扩展，`h2` 代表 HTTP/2 作为第一个 ALPN——随后是两个截断的哈希，分别是排序后的密码和扩展加签名算法。JA4 是一个家族中的一员：JA4S 指纹识别服务器，JA4H 指纹识别 HTTP 请求头，JA4X 指纹识别证书，JA4T 指纹识别原始 TCP 层。

![两个客户端与同一网站建立 TLS 握手：默认的 Python 客户端被其 JA3/JA4 指纹识别，在发送任何 HTTP 之前就收到 403，而模仿浏览器的客户端通过握手并收到 200；卡片显示，请求头要在握手检查之后才会被读取。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335157/blog/generated/qp-b9-019-diag1.png)
*JA3/JA4 查找发生在握手时——不匹配的指纹在您的请求头被读取之前就是 403。*

## 为什么干净的 IP 无法拯救您

这就是让只投资于代理的人困惑的部分。一个干净的住宅 IP 告诉网站流量来自真实网络。一个与浏览器不匹配的 TLS 握手则告诉它流量来自脚本。当这两个信号不一致时，握手胜出，因为它更难被意外伪造。即使您轮换一千个干净的出口，如果所有一千个都携带相同的 `python-requests` JA3，您仍然会在每次请求中失败。IP 和指纹是独立的轴——您必须同时正确。

具体来说，这在针对 Cloudflare 保护目标的通过率中表现出来：默认的 `requests` 客户端大约能通过两百分之一的请求，使用 HTTP/2 的 `httpx` 稍微好一些，而与浏览器匹配的客户端则进入中等偏高的通过率。每种情况下使用的都是相同的 IP 池。变量是 TLS 栈。Akamai 通过将 TLS 指纹与 HTTP/2 指纹结合起来进一步提高了门槛——SETTINGS 帧值、窗口大小和流优先级——因此即使正确的 JA3 也可能被捕获，如果 HTTP/2 层看起来像脚本。我们对 [为什么 Akamai 阻止大多数代理流量](https://quantumproxies.io/blog/why-akamai-bot-manager-blocks-most-proxy-traffic-in-2026) 的分析深入探讨了该栈。

![面对受 Cloudflare 保护的目标、所有客户端使用同一个干净 IP 池时的通过率：默认的 python-requests 约为 2%，使用 HTTP/2 的 httpx 略高，无头浏览器自动化约为百分之六十，而匹配浏览器的 curl_cffi 客户端约为百分之八十五；卡片指出，解决之道在于握手。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335157/blog/generated/qp-b9-019-diag2.png)
*每种情况下都是相同的干净 IP——只有 TLS 栈改变了通过率。解决方法是握手，而不是出口。*

## 真正通过的伪装选项

您不能通过调整密码字符串将浏览器握手附加到标准客户端上——OpenSSL 和 urllib3 的选项并未暴露 JA3 读取的每个参数。有效的方法是使用一个能够说出浏览器确切 TLS 方言的客户端：

- **curl_cffi**（Python）——基于 curl-impersonate 的绑定。一个参数，`impersonate="chrome120"`，您的 JA3 就与真实的 Chrome 匹配。
- **curl-impersonate**——一个修补过的 curl 二进制文件，从命令行重现 Chrome 和 Firefox 的 TLS 和 HTTP 栈。
- **tls-client**（Go）——一个在握手级别可选择浏览器配置文件（Chrome、Firefox、Safari）的 HTTP 客户端。
- **真实浏览器自动化**——Playwright、Puppeteer 或 Selenium 使用实际的浏览器引擎，因此 JA3 是真实的。问题在于其他标志（如 `navigator.webdriver` 标志、canvas 和 WebGL）仍然会泄露，因此通过率在没有额外强化的情况下约为 60%。

```python
# curl_cffi: a browser-shaped handshake in two lines
from curl_cffi import requests

session = requests.Session(impersonate="chrome120")
r = session.get(
    "https://example.com",
    proxies={"https": "http://USER:PASS@residentialpremium.quantumproxies.io:8080"},
    timeout=20,
)
print(r.status_code)  # matched JA3 + a clean residential exit
```

注意代码片段中的代理。伪装和干净出口是互补的，而不是替代品：浏览器形状的握手让您通过 TLS 检查，而 [干净的住宅 IP](https://quantumproxies.io/residential-proxies) 使请求不被列入信誉封锁名单。将它们配对，您就能同时修复两个轴。

[使用 Scraper API 跳过 TLS 军备竞赛](https://quantumproxies.io/scraper-api)

## 何时将整个问题交给他人

DIY 伪装有效，直到目标轮换其检测，添加 HTTP/2 指纹或需要 JavaScript 执行——然后您就像在维护一个浏览器仿真库作为第二份工作。一个 [Scraper API](https://quantumproxies.io/scraper-api) 携带真实的、旋转的浏览器指纹，匹配 HTTP/2 层，按需运行 JS，并从单次调用中返回干净的 HTML、markdown 或 JSON。它将 TLS 军备竞赛变成他人的问题，而您可以专注于数据。有关网站如何标记自动化的更广泛图景，请参阅我们的 [每个代理检测信号](https://quantumproxies.io/blog/how-websites-detect-proxies) 指南。

## 常见问题解答

### 什么是 JA3 指纹？

JA3 指纹是从 TLS ClientHello 中提取的五个字段的 MD5 哈希——TLS 版本、密码套件、扩展、椭圆曲线和点格式。因为每个软件栈对这些字段的排序不同，哈希在交换任何 HTTP 数据之前就能识别客户端（Chrome、Firefox、curl、Python）。

### JA3 和 JA4 有什么区别？

JA3 使用 MD5 对五个 ClientHello 字段进行哈希，当浏览器随机化扩展顺序时会失效。JA4 来自 FoxIO，在哈希前对字段进行排序，因此可以在随机化中幸存下来，添加了一个人类可读的元数据前缀，并涵盖 QUIC/HTTP/3、ALPN 和签名算法。JA4 是更可靠的现代信号；JA3N 是 JA3 的标准化权宜之计。

### 我能仅通过代理绕过 TLS 指纹识别吗？

不能。代理改变的是 IP，而不是握手。如果您的 TLS 指纹与已知的抓取库匹配，轮换出口也无济于事——每个请求仍然携带机器人的签名。您需要一个能够重现浏览器 TLS 栈的客户端，然后在上面使用干净的代理以获得 IP 信誉。

### Python requests 有可检测的 TLS 指纹吗？

有。`requests` 使用 urllib3，其密码和扩展顺序独特，反机器人供应商已经将其编入目录，因此其 JA3 是已知机器人的签名。切换到带有 `impersonate` 配置文件的 `curl_cffi` 以发送与浏览器匹配的握手。

TLS 指纹识别将战斗移到了 HTTP 以下，这就是为什么请求头技巧和 IP 轮换不再单独足够。匹配握手，保持出口干净，403-on-request-one 问题就会消失。这是技术指导，而不是忽视网站条款的许可——始终在法律和目标规则范围内进行抓取。

[让 Scraper API 处理 JA3、JA4 和 HTTP/2](https://quantumproxies.io/scraper-api)
