CAPSOLVER
博客
如何使用CapSolver解决n8n中的TLS指纹识别问题

如何使用CapSolver解决n8n中的TLS指纹问题

Logo of CapSolver

Emma Foster

Machine Learning Engineer

18-Mar-2026

如果你曾经尝试过抓取受企业级反机器人检测保护的网站,你可能遇到了一个看不见的障碍:即使你的请求头、Cookie和User-Agent都完美无缺,请求仍会被阻止。原因在于TLS指纹识别——它发生在你的HTTP请求发送之前。

像Cloudflare、Akamai、DataDome等反机器人服务会检查原始的TLS握手,以确定客户端是真实浏览器还是自动化工具。标准HTTP客户端——Go的net/http、Python的requests、curl、Node.js的axios——都有独特的TLS指纹,会立即被标记。

在本指南中,你将使用**httpcloak构建一个轻量级的Go服务器**,模拟真实的Chrome TLS指纹,并将其连接到你的n8n工作流,使每条HTTP请求在网络安全层面看起来都像真实的Chrome浏览器流量。


什么是TLS指纹识别?

每次客户端通过HTTPS连接到网站时,它会通过发送一个ClientHello消息启动TLS握手。此消息包含:

  • 密码套件——客户端支持的加密算法及其顺序
  • TLS扩展——如SNI、ALPN、支持的组、签名算法等特性
  • 椭圆曲线和点格式——加密参数
  • TLS版本——客户端支持的最大TLS版本

反机器人服务会提取这些值并计算一个指纹——称为JA3JA4指纹——该指纹唯一标识客户端软件。每个浏览器、HTTP库和编程语言运行时都会产生不同的指纹。

客户端 JA3指纹 被识别为
Chrome 145 与Chrome的密码套件顺序匹配的唯一哈希 真实浏览器
Firefox 130 不同的哈希——Firefox使用不同的密码套件偏好 真实浏览器
Go net/http 完全不同的哈希——Go的TLS栈显而易见 机器人/自动化工具
Python requests 另一个独特的哈希——Python的urllib3 TLS可被识别 机器人/自动化工具
curl 又一个不同的哈希——curl的TLS指纹广为人知 机器人/自动化工具
Node.js axios Node.js的TLS指纹——很容易被标记 机器人/自动化工具

关键洞察:TLS指纹识别发生在握手期间,在任何HTTP头发送之前。 无论你如何修改请求头,都无法修复非浏览器的TLS指纹。


为什么标准HTTP客户端会失败?

当浏览器通过HTTPS连接到网站时,它会发送一个包含其支持的密码套件、扩展和设置的TLS ClientHello。反机器人服务会记录此指纹(称为JA3或JA4指纹),并将其与已知的浏览器配置文件进行比较。

Go的net/http、Python的requests、curl和大多数HTTP库都有独特的TLS指纹。即使Cookie和头信息正确,如果反机器人系统检测到非浏览器的TLS指纹,也会阻止请求。

以下是逐步发生的过程:

  1. 你的n8n工作流向受保护的网站发送HTTP请求
  2. TLS握手开始——你的客户端发送其ClientHello
  3. 反机器人服务从握手中记录JA3/JA4指纹
  4. 指纹匹配Go/Python/Node.js——而不是Chrome或Firefox
  5. 请求被阻止、挑战或返回一个假页面——在你的头信息被评估之前

这就是为什么设置User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...没有帮助的原因。User-Agent是HTTP层的头信息。TLS指纹识别发生在更低的层级。如果你的User-Agent显示Chrome,但你的TLS指纹显示Go,请求会立即被标记。


哪些人使用TLS指纹识别?

TLS指纹识别已成为企业级反机器人保护的标准实践。以下是检查TLS指纹的主要服务:

反机器人服务 TLS检查 说明
Cloudflare Bot Management 全页面“验证你的浏览器...”挑战。对每条请求检查JA3/JA4
Akamai Bot Manager 使用TLS指纹作为机器人评分的多个信号之一
DataDome 在分析TLS指纹的同时结合行为信号
许多其他服务 各不相同 TLS指纹识别正在成为企业级反机器人保护的标准

CapSolver 支持解决这些服务中的许多挑战。本指南中的TLS服务器设计用于与任何验证码解决工作流配合使用,当最终HTTP获取需要看起来像真实浏览器时——无论你是绕过Cloudflare挑战、Akamai、DataDome还是其他反机器人系统。


前提条件

要求 说明
n8n自托管 必需——TLS服务器必须与n8n运行在同一台机器上。n8n Cloud不适用。
Go 1.21+ 必须安装在服务器上。通过go version检查。
进程管理器 (推荐) 任何进程管理器(systemd、supervisor、Docker、PM2)以在重启后保持TLS服务器运行

步骤1 —— 构建TLS服务器

TLS服务器是一个轻量级的Go HTTP服务器,监听端口7878,并通过httpcloak的Chrome-145 TLS预设转发请求。

创建源文件

bash 复制代码
mkdir -p ~/tls-server && cd ~/tls-server

创建一个名为main.go的文件,内容如下:

go 复制代码
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/sardanioss/httpcloak/client"
)

type FetchRequest struct {
	URL     string            `json:"url"`
	Method  string            `json:"method"`
	Headers map[string]string `json:"headers"`
	Proxy   string            `json:"proxy"`
	Body    string            `json:"body"`
}

type FetchResponse struct {
	Status  int                 `json:"status"`
	Body    string              `json:"body"`
	Headers map[string][]string `json:"headers"`
}

type ErrorResponse struct {
	Error string `json:"error"`
}

func writeError(w http.ResponseWriter, status int, msg string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(ErrorResponse{Error: msg})
}

func fetchHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeError(w, http.StatusMethodNotAllowed, "only POST allowed")
		return
	}

	var req FetchRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
		return
	}

	if req.URL == "" {
		writeError(w, http.StatusBadRequest, "url is required")
		return
	}
	if req.Method == "" {
		req.Method = "GET"
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	c := client.NewClient("chrome-145", client.WithTimeout(60*time.Second))
	defer c.Close()

	if req.Proxy != "" {
		c.SetProxy(req.Proxy)
	}

	headers := make(map[string][]string, len(req.Headers))
	var userAgent string
	for k, v := range req.Headers {
		lower := strings.ToLower(k)
		if lower == "user-agent" {
			userAgent = v
		} else {
			headers[k] = []string{v}
		}
	}

	var bodyReader io.Reader
	if req.Body != "" {
		bodyReader = strings.NewReader(req.Body)
	}

	hcReq := &client.Request{
		Method:    strings.ToUpper(req.Method),
		URL:       req.URL,
		Headers:   headers,
		Body:      bodyReader,
		UserAgent: userAgent,
		FetchMode: client.FetchModeNavigate,
	}

	resp, err := c.Do(ctx, hcReq)
	if err != nil {
		writeError(w, http.StatusBadGateway, "fetch failed: "+err.Error())
		return
	}

	body, err := resp.Text()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "read body failed: "+err.Error())
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(FetchResponse{
		Status:  resp.StatusCode,
		Body:    body,
		Headers: resp.Headers,
	})
}

func main() {
	const port = "7878"

	mux := http.NewServeMux()
	mux.HandleFunc("/fetch", fetchHandler)
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		fmt.Fprint(w, `{"status":"ok"}`)
	})

	log.Printf("TLS server (httpcloak chrome-145) listening on :%s", port)
	log.Fatal(http.ListenAndServe(":"+port, mux))
}

初始化并构建

bash 复制代码
go mod init tls-server
go get github.com/sardanioss/httpcloak/client
go build -o main main.go

运行服务器

bash 复制代码
./main

服务器在前台运行。要使其在后台运行,请使用任何进程管理器(systemd、supervisor、Docker等)或在screen/tmux会话中运行。

验证是否运行(在新终端中)

bash 复制代码
curl http://localhost:7878/health

预期结果:{"status":"ok"}

注意: TLS服务器必须运行在与你的n8n实例同一台机器上。n8n工作流会通过http://localhost:7878/fetch调用它。


步骤2 —— 允许n8n调用本地主机

默认情况下,n8n会阻止HTTP请求节点调用本地主机地址(SSRF保护)。你需要禁用此功能,以便你的工作流可以访问localhost:7878上的TLS服务器。

添加环境变量N8N_BLOCK_ACCESS_TO_LOCALHOST=false并重启你的n8n实例。如何操作取决于你运行n8n的方式:

如果你直接运行n8n:

bash 复制代码
export N8N_BLOCK_ACCESS_TO_LOCALHOST=false
n8n start

如果你使用Docker:

docker run命令中添加-e N8N_BLOCK_ACCESS_TO_LOCALHOST=false,或在docker-compose.ymlenvironment部分添加它。


步骤3 —— 从n8n使用TLS服务器

TLS服务器提供了一个单一端点,接受任何HTTP请求并使用Chrome TLS指纹转发它。

API参考

端点: POST http://localhost:7878/fetch

请求体(JSON):

json 复制代码
{
  "url": "https://example.com",
  "method": "GET",
  "headers": {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
    "cookie": "cf_clearance=abc123; session=xyz"
  },
  "proxy": "http://user:pass@host:port",
  "body": ""
}
字段 类型 是否必需 说明
url 字符串 要获取的目标URL
method 字符串 HTTP方法——默认为GET
headers 对象 要发送的HTTP头的键值对
proxy 字符串 http://user:pass@host:port格式的代理URL
body 字符串 请求体(用于POST/PUT请求)

响应(JSON):

json 复制代码
{
  "status": 200,
  "body": "<html>...</html>",
  "headers": { "content-type": ["text/html"], "..." : ["..."] }
}

配置n8n HTTP请求节点

要在n8n工作流中调用TLS服务器,请使用以下设置的HTTP请求节点:

参数 说明
方法 POST 始终向TLS服务器发送POST
URL http://localhost:7878/fetch 本地TLS服务器端点
内容类型 Raw 不要使用JSON——n8n的JSON模式会错误地序列化
原始内容类型 application/json 告诉TLS服务器正文是JSON
正文 ={{ JSON.stringify({ url: "...", method: "GET", headers: {...}, proxy: "..." }) }} 要转发的实际请求

重要提示: 在正文中使用contentType: "json"JSON.stringify()会导致n8n双重序列化,发送{"": ""}而不是你的数据。始终使用contentType: "raw"rawContentType: "application/json"

示例:获取受保护的页面

在HTTP请求节点的正文表达式中:

javascript 复制代码
={{ JSON.stringify({
  url: "https://protected-site.com/data",
  method: "GET",
  headers: {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "accept-language": "en-US,en;q=0.9"
  },
  proxy: "http://user:pass@proxy-host:8080"
}) }}

TLS服务器将使用Chrome-145 TLS指纹转发此请求,目标网站将看到真实的Chrome浏览器连接。


测试它

直接从命令行测试TLS服务器:

bash 复制代码
curl -X POST http://localhost:7878/fetch \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tls-check.example.com",
    "method": "GET",
    "headers": {
      "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
    }
  }'

你可以通过将服务器指向JA3/JA4指纹检查器来验证你的TLS指纹——结果应与真实Chrome浏览器匹配,而不是Go标准库客户端。


导入此工作流

此工作流创建了一个Webhook端点,通过TLS服务器转发任何请求,使用Chrome TLS指纹。发送一个包含urlmethodheaders和可选proxy的POST请求——工作流将其传递给localhost:7878/fetch并返回结果。

复制代码
Webhook (POST /tls-fetch) → 通过TLS服务器获取 → 向Webhook响应

复制以下JSON并使用菜单 → 从JSON导入将其导入n8n。

点击展开工作流JSON
json 复制代码
{
  "name": "TLS Fetch — Chrome Fingerprint Proxy",
  "nodes": [
    {
      "parameters": {
        "content": "## TLS Fetch — Chrome Fingerprint Proxy\n\n**谁适用:** 需要带有真实浏览器TLS指纹的HTTP请求的开发者。\n\n**功能:** 通过Go TLS服务器(httpcloak)代理HTTP请求,模拟Chrome的TLS指纹,绕过机器人检测。\n\n**工作原理:**\n1. Webhook接收目标URL和请求详情\n2. 请求通过本地TLS服务器转发,带有Chrome指纹\n3. 响应返回给调用者\n\n**设置:**\n1. 确保TLS服务器(httpcloak)在端口7878上运行\n2. 启用工作流\n3. 向Webhook URL发送POST请求,附上你的请求详情",
        "height": 494,
        "width": 460,
        "color": 1
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -720,
        -300
      ],
      "id": "sticky-blog-main-1773678228122-1",
      "name": "便签"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tls-fetch",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -200,
        0
      ],
      "id": "tls00001-0001-0001-0001-000000000001",
      "name": "接收求解请求",
      "webhookId": "tls00001-aaaa-bbbb-cccc-000000000001"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://localhost:7878/fetch",
        "sendBody": true,
"contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify($json.body) }}",
        "options": {
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        100,
        0
      ],
      "id": "tls00001-0001-0001-0001-000000000002",
      "name": "通过 TLS 服务器获取"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        400,
        0
      ],
      "id": "tls00001-0001-0001-0001-000000000003",
      "name": "响应 Webhook"
    }
  ],
  "connections": {
    "接收求解器请求": {
      "main": [
        [
          {
            "node": "通过 TLS 服务器获取",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "通过 TLS 服务器获取": {
      "main": [
        [
          {
            "node": "响应 Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  }
}

结论

您已经设置了一个 TLS 指纹欺骗服务器,使 n8n 的 HTTP 请求在网络安全层看起来像真实的 Chrome 浏览器流量。这对于绕过检查 TLS 指纹的反机器人服务保护的网站至关重要。

此 TLS 服务器可用于绕过:

  • Cloudflare Bot Management — 检查 TLS 指纹的全页挑战
  • Akamai Bot Manager — 使用 TLS 分析的企业级机器人检测
  • DataDome — 行为 + TLS 指纹分析
  • PerimeterX / HUMAN — 设备 + TLS 指纹识别
  • 许多其他 CapSolver 支持的反机器人服务

httpcloak 库及其 Chrome-145 预设处理 JA3/JA4 指纹欺骗、HTTP/2 SETTINGS 帧、ALPN 协商和头顺序 — 使您的请求在 TLS 层无法与真实 Chrome 浏览器区分。


需要在 TLS 欺骗的同时解决 CAPTCHAs? 请查看 CapSolver — 它直接集成到 n8n 中作为官方节点,支持 Cloudflare 挑战、Turnstile、reCAPTCHA 等多种服务。使用优惠码 n8n 在首次充值时额外获得 8% 的奖励!

CapSolver 奖励代码横幅

常见问题

什么是 TLS 指纹识别?

TLS 指纹识别是一种技术,服务器分析您的 TLS ClientHello 消息的特征 — 包括密码套件、扩展及其顺序 — 以识别发起连接的软件。每个 HTTP 客户端(Chrome、Firefox、curl、Go、Python)都有独特的指纹模式。

为什么不能仅将 User-Agent 头设置为 Chrome?

User-Agent 头是 HTTP 层的属性。TLS 指纹识别发生在更低的层次 — 在发送任何 HTTP 头之前进行 TLS 握手。反机器人服务会同时比较这两层:如果您的 User-Agent 显示 Chrome 但 TLS 指纹显示 Go/Python,则请求会被标记为机器人。

什么是 httpcloak?

httpcloak 是一个 Go 库,用于欺骗真实的浏览器 TLS 配置。它处理 JA3/JA4 指纹匹配、HTTP/2 SETTINGS 帧、ALPN 协商和头顺序。chrome-145 预设使连接与真实 Chrome 145 浏览器无法区分。

可以使用其他 Chrome 版本预设吗?

可以。httpcloak 支持多个浏览器预设。请查看 httpcloak 文档 以获取可用预设。要更改预设,请将 client.NewClient("chrome-145", ...) 修改为 main.go 中的所需浏览器配置。

这与 n8n Cloud 兼容吗?

不太容易。TLS 服务器是一个本地 Go 二进制文件,必须与 n8n 在同一台机器上运行,以便工作流可以调用 http://localhost:7878/fetch。n8n Cloud 不允许在工作流旁边运行本地服务。您需要自托管的 n8n 实例。

可以在其他机器上运行 TLS 服务器吗?

可以,但需要将 n8n HTTP 请求节点中的 URL 从 http://localhost:7878/fetch 更改为 http://your-server-ip:7878/fetch,并确保端口 7878 可访问。您还需要禁用 n8n 的 SSRF 保护或白名单服务器的 IP。

如何在新版本发布时更新 Chrome 预设?

更新 httpcloak 依赖项:go get -u github.com/sardanioss/httpcloak/client,将 main.go 中的预设字符串更改为新版本,使用 go build -o main main.go 重新构建,并重启服务器。

TLS 服务器支持并发请求吗?

支持。Go 的 HTTP 服务器原生处理并发请求。每个请求都会创建一个新的 httpcloak 客户端实例和自己的 TLS 连接。对于高负载工作,需监控内存使用情况,因为每个连接都会维护自己的 TLS 状态。

性能开销是多少?

TLS 服务器添加的延迟很小 — 通常本地代理跳转需要 10-50ms。大部分请求时间花费在实际的 HTTPS 连接到目标上。Chrome TLS 握手比 Go 的默认设置稍重,但在实践中可以忽略。

如何在服务器重启后保持 TLS 服务器运行?

使用任何进程管理器 — systemd、supervisor、Docker 或类似工具 — 将 TLS 服务器注册为开机启动的服务。快速设置的话,也可以在 screentmux 会话中运行它。

合规声明: 本博客提供的信息仅供参考。CapSolver 致力于遵守所有适用的法律和法规。严禁以非法、欺诈或滥用活动使用 CapSolver 网络,任何此类行为将受到调查。我们的验证码解决方案在确保 100% 合规的同时,帮助解决公共数据爬取过程中的验证码难题。我们鼓励负责任地使用我们的服务。如需更多信息,请访问我们的服务条款和隐私政策。

更多