
Anh Tuan
Data Science Expert

Bảo vệ bot của Cloudflare vượt xa các widget CAPTCHA thông thường. Cloudflare Challenge — màn hình toàn trang "Đang xác minh trình duyệt của bạn…" chặn hoàn toàn việc truy cập vào một trang web — là một trong những biện pháp bảo vệ bot mạnh mẽ nhất trên web. Nó chạy một thử thách JavaScript đầy đủ trong môi trường trình duyệt thật, kiểm tra các tín hiệu hành vi, và nhận dạng dấu vân tay TLS của bạn trước khi cho phép bạn truy cập.
Các công cụ tự động hóa tiêu chuẩn đều thất bại ở đây. Không phải vì chúng không thể giải thử thách, mà bởi vì Cloudflare nhận dạng dấu vân tay quá trình bắt tay TLS — và bất kỳ khách HTTP nào không phải trình duyệt đều bị chặn trước khi thử thách được phục vụ, hoặc bị thử thách lại ngay sau đó, ngay cả khi có cookie cf_clearance hợp lệ.
Trong hướng dẫn này, bạn sẽ học cách xây dựng một trình thu thập dữ liệu Cloudflare Challenge trong n8n mà thực sự hoạt động — kết hợp CapSolver để giải thử thách, một server TLS Go cục bộ để giả mạo dấu vân tay TLS của Chrome, và một workflow n8n để liên kết tất cả lại.
Bạn sẽ xây dựng:
Cloudflare Challenge (còn gọi là thử thách JS hoặc thử thách Bot Management) là một trang trung gian toàn trang mà Cloudflare chèn vào trước khi khách truy cập có thể vào trang web được bảo vệ. Bạn chắc đã thấy nó: một màn hình đen hoặc trắng với dòng chữ "Đang xác minh trình duyệt của bạn…" hoặc "Chờ một chút…" cùng với thanh tải hoặc logo Cloudflare.
Khác với Turnstile — là widget nhỏ được nhúng bên trong trang — Cloudflare Challenge chiếm toàn bộ trang. Bạn không thể xem nội dung nào cho tới khi nó hoàn thành.
| Cloudflare Challenge | Cloudflare Turnstile | |
|---|---|---|
| Nơi xuất hiện | Trang trung gian toàn trang — chặn hoàn toàn truy cập trang web | Widget nhúng bên trong trang (ví dụ: biểu mẫu đăng nhập) |
| Diễn giải | Màn hình tải "Đang xác minh trình duyệt của bạn…" | Một ô checkbox nhỏ hoặc widget vô hình trong biểu mẫu |
| Ai thêm | Cloudflare tự động thêm dựa trên quy tắc bảo mật | Chủ sở hữu trang web nhúng trong HTML của họ |
| Cách giải quyết | AntiCloudflareTask — yêu cầu proxy |
AntiTurnstileTaskProxyLess — không cần proxy |
| Cookie trả về | cf_clearance (theo domain, giới hạn IP) |
Token Turnstile (thời gian sống ngắn, dùng một lần) |
| Có cần proxy? | Có — cùng IP phải dùng cho việc giải và lấy dữ liệu | Không |
Nếu bạn thấy một ô checkbox hoặc widget nhúng trong biểu mẫu, đó là Turnstile — không phải thử thách này. Hãy xem hướng dẫn của CapSolver về cách nhận dạng loại thử thách nếu bạn không chắc chắn.
Đây là vấn đề mà hầu hết các hướng dẫn bỏ qua: Cloudflare kiểm tra dấu vân tay TLS của bạn, không chỉ cookie.
Khi trình duyệt kết nối đến một trang web qua HTTPS, nó gửi một TLS ClientHello bao gồm thông tin về bộ mã hóa được hỗ trợ, các phần mở rộng và thiết lập. Cloudflare ghi lại dấu vân tay này (gọi là dấu vân tay JA3 hoặc JA4) và so sánh với các hồ sơ trình duyệt đã biết.
net/http của Go, requests của Python, curl và hầu hết các thư viện HTTP đều có dấu vân tay TLS riêng biệt. Dù CapSolver giải thành công thử thách và trả về cookie cf_clearance hợp lệ, Cloudflare vẫn sẽ thử thách lại hoặc chặn yêu cầu nếu phát hiện dấu vân tay TLS không phải trình duyệt trong lần lấy tiếp theo.
Giải pháp: server Go dùng httpcloak — thư viện giả mạo toàn bộ stack TLS Chrome thật, bao gồm:
Điều này làm cho request lấy dữ liệu giống hệt một yêu cầu trình duyệt Chrome ở cấp mạng.
| Yêu cầu | Ghi chú |
|---|---|
| n8n tự host | Bắt buộc — server TLS phải chạy trên cùng máy với n8n. n8n Cloud không thích hợp cho trường hợp này. |
| Tài khoản CapSolver | Đăng ký tại đây và lấy API key |
| Go 1.21+ | Phải được cài trên máy server n8n. Kiểm tra bằng go version. |
| Proxy dân cư hoặc di động | Proxy trung tâm dữ liệu sẽ không thành công với hầu hết site được bảo vệ bởi Cloudflare. Xem Yêu cầu proxy bên dưới. |
CapSolver có sẵn như một tích hợp chính thức trong n8n — không cần cài node bên ngoài.
Đi tới instance n8n của bạn và chọn Settings → Credentials.

AllBạn sẽ thấy banner màu xanh lá "Connection tested successfully".

Quan trọng: Mỗi node CapSolver trong workflow của bạn sẽ tham chiếu đến credential này. Bạn chỉ cần tạo một lần — tất cả workflow giải thử thách của bạn sẽ dùng chung credential này.
Server Go này nhận các yêu cầu lấy dữ liệu từ n8n và thực thi chúng dùng profile TLS Chrome của httpcloak. Nó là một chương trình nhỏ, độc lập, bạn chạy cùng n8n.
Tạo thư mục và lưu file dưới tên main.go:
mkdir -p ~/tls-server && cd ~/tls-server
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)
}
// Extract user-agent separately so httpcloak uses it instead of its preset value.
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))
}
go mod init tls-server
go get github.com/sardanioss/httpcloak/client
go build -o main main.go
./main
curl http://localhost:7878/health
Kết quả mong đợi: {"status":"ok"}
Lưu ý: Server TLS phải chạy trên cùng máy với instance n8n của bạn. Workflow n8n gọi nó tại
http://localhost:7878/fetch. Nếu bạn dùng n8n Cloud, bạn sẽ cần một thiết lập tự host — đây cũng là một lý do khuyến nghị dùng n8n tự host cho thu thập dữ liệu Cloudflare Challenge.
Mặc định, n8n chặn các node HTTP Request gọi địa chỉ localhost (để chống SSRF). Bạn cần tắt giới hạn này.
Thêm biến môi trường N8N_BLOCK_ACCESS_TO_LOCALHOST=false và khởi động lại instance n8n của bạn. Cách thực hiện phụ thuộc cách bạn chạy n8n:
Nếu bạn chạy n8n trực tiếp:
export N8N_BLOCK_ACCESS_TO_LOCALHOST=false
n8n start
Nếu bạn dùng Docker:
Thêm -e N8N_BLOCK_ACCESS_TO_LOCALHOST=false vào lệnh docker run, hoặc thêm vào phần environment trong file docker-compose.yml.
Workflow này tạo một endpoint POST nhận URL được bảo vệ Cloudflare và proxy, giải thử thách qua CapSolver, và trả về cookie cf_clearance thô và userAgent. Không cần server TLS — ứng dụng bạn sẽ tự thực hiện việc fetch.
Webhook (POST /solver-cloudflare-challenge) → Cloudflare Challenge (CapSolver)
→ Định dạng giải pháp → Trả lời Webhook
4 node, chỉ webhook, không có đường lịch trình, không phụ thuộc server TLS.
websiteURL và proxyAntiCloudflareTaskcookies thành chuỗi cookie, xử lý lỗi với continueOnFailcf_clearance, chuỗi cookie đã chuẩn hóa, và userAgent### Cấu hình Node| Cài đặt | Giá trị |
|---|---|
| HTTP Method | POST |
| Path | solver-cloudflare-challenge |
| Respond | Response Node |
Điều này sẽ tạo một endpoint tại: https://your-n8n-instance.com/webhook/solver-cloudflare-challenge
| Tham số | Giá trị | Mô tả |
|---|---|---|
| Operation | Cloudflare Challenge |
Chọn AntiCloudflareTask |
| Type | AntiCloudflareTask |
Thử thách Cloudflare toàn trang |
| Website URL | ={{ $json.body.websiteURL }} |
URL được bảo vệ bởi Cloudflare |
| Proxy | ={{ $json.body.proxy }} |
Proxy dân cư ở định dạng host:port:user:pass |
| Continue On Fail | true |
Trả về lỗi có cấu trúc thay vì bị crash |
CapSolver sẽ khởi chạy trình duyệt thực thông qua proxy của bạn, tải URL mục tiêu và giải thử thách Cloudflare. Khi thành công, nó trả về một đối tượng solution chứa:
cookies — một đối tượng { cf_clearance: "..." } với cookie clearanceuserAgent — chuỗi User-Agent chính xác mà trình duyệt đã sử dụng trong quá trình giảiLưu ý: Trường là
websiteURL(không phảitargetURL) — phù hợp với tên trường được sử dụng bởi tất cả các API Solver khác và chính node CapSolver.
Node này cần thiết vì AntiCloudflareTask trả về cookies dưới dạng đối tượng ({ cf_clearance: "..." }), không phải một chuỗi token đơn giản như reCAPTCHA hoặc Turnstile. Nó sẽ tuần tự hóa cookie, trích xuất cf_clearance, và trả về lỗi có cấu trúc nếu CapSolver thất bại.
const input = $input.first().json;
if (input.error || !input.data || !input.data.solution) {
const errorMsg = input.error
? (input.error.message || JSON.stringify(input.error))
: 'No solution returned — site may not be showing a challenge';
return [{ json: { success: false, error: errorMsg } }];
}
const solution = input.data.solution;
const cookies = solution.cookies;
const cfClearance = (cookies && typeof cookies === 'object')
? (cookies.cf_clearance || '') : '';
const cookieString = (cookies && typeof cookies === 'object')
? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')
: (cookies || '');
return [{ json: {
success: true,
cf_clearance: cfClearance,
cookies: cookieString,
userAgent: solution.userAgent || '',
solvedAt: new Date().toISOString()
}}];
| Cài đặt | Giá trị |
|---|---|
| Respond With | JSON |
| Response Body | ={{ JSON.stringify($json) }} |
curl -X POST https://your-n8n-instance.com/webhook/solver-cloudflare-challenge \
-H "Content-Type: application/json" \
-d '{
"websiteURL": "https://protected-site.com",
"proxy": "host:port:user:pass"
}'
Phản hồi thành công:
{
"success": true,
"cf_clearance": "abc123...",
"cookies": "cf_clearance=abc123...",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/145.0.0.0 ...",
"solvedAt": "2026-03-11T12:00:00.000Z"
}
Phản hồi thất bại (không tìm thấy thử thách, proxy không hợp lệ, v.v.):
{
"success": false,
"error": "No solution returned — site may not be showing a challenge"
}
Sao chép JSON bên dưới và nhập vào n8n qua Menu → Import from JSON. Sau khi nhập, chọn thông tin xác thực CapSolver của bạn trong node Cloudflare Challenge.
{
"nodes": [
{
"parameters": {
"content": "## Cloudflare Challenge \u2014 Solver API\n\n### How it works\n\n1. Receives a request via webhook to solve a Cloudflare challenge.\n2. Uses the Cloudflare Challenge node to attempt solving the challenge.\n3. Formats the solved challenge token via custom code.\n4. Responds back to the original request with the solved token.\n\n### Setup steps\n\n- [ ] Configure the Webhook node to receive requests.\n- [ ] Set up CapSolver credentials to enable solving Cloudflare challenges.\n- [ ] Ensure the Response node is properly configured to send back the solved token.\n\n### Customization\n\nYou can customize the code in the 'Format Solution' node to change the formatting of the solved token response.",
"width": 480,
"height": 672
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-816,
-128
],
"id": "254f3829-0e6e-4ae3-bf83-85851be9a7bc",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Receive and solve challenge\n\nTriggers on a new request and attempts to solve the Cloudflare challenge.",
"width": 512,
"height": 304,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-256,
-128
],
"id": "44e79738-a2f0-41ec-8ff0-514afbd6cc45",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Format and return solution\n\nFormats the solution and responds back with the solved token.",
"width": 496,
"height": 304,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
352,
-128
],
"id": "fc40112d-389d-4d4e-8872-af2ae533c513",
"name": "Sticky Note2"
},
{
"parameters": {
"httpMethod": "POST",
"path": "solver-cloudflare-challenge",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-208,
0
],
"id": "cf770001-7777-7777-7777-777777777701",
"name": "Receive Solver Request",
"webhookId": "cf770001-aaaa-bbbb-cccc-777777777701",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.body.websiteURL }}",
"proxy": "={{ $json.body.proxy }}",
"userAgent": "={{ $json.body.userAgent }}",
"html": "={{ $json.body.html }}"
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
112,
0
],
"id": "cf770001-7777-7777-7777-777777777702",
"name": "Cloudflare Challenge",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver account"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const input = $input.first().json;\n\nif (input.error || !input.data || !input.data.solution) {\n const errorMsg = input.error\n ? (input.error.message || JSON.stringify(input.error))\n : 'No solution returned \u2014 site may not be showing a challenge';\n return [{ json: { success: false, error: errorMsg } }];\n}\n\nconst solution = input.data.solution;\nconst cookies = solution.cookies;\nconst cfClearance = (cookies && typeof cookies === 'object') ? (cookies.cf_clearance || '') : '';\nconst cookieString = (cookies && typeof cookies === 'object')\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n\nreturn [{ json: {\n success: true,\n cf_clearance: cfClearance,\n cookies: cookieString,\n userAgent: solution.userAgent || '',\n solvedAt: new Date().toISOString()\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
0
],
"id": "cf770001-7777-7777-7777-777777777703",
"name": "Format Solution"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
700,
0
],
"id": "cf770001-7777-7777-7777-777777777704",
"name": "Return Solved Token"
}
],
"connections": {
"Receive Solver Request": {
"main": [
[
{
"node": "Cloudflare Challenge",
"type": "main",
"index": 0
}
]
]
},
"Cloudflare Challenge": {
"main": [
[
{
"node": "Format Solution",
"type": "main",
"index": 0
}
]
]
},
"Format Solution": {
"main": [
[
{
"node": "Return Solved Token",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Cho đến nay, API Solver bên trên chỉ cho thấy cách để nhận cookie cf_clearance và userAgent đã được giải. Nhưng bạn thực sự làm gì với chúng?
Khác với reCAPTCHA hoặc Turnstile, nơi bạn gửi token trong trường form, Thử thách Cloudflare trả về một cookie (cf_clearance) phải được gửi dưới dạng header trong mỗi yêu cầu tiếp theo. Cookie này được liên kết với IP proxy và User-Agent đã sử dụng trong quá trình giải — cả hai phải giống hệt nhau khi fetch.
Mẫu chung như sau:
cf_clearance và userAgent từ CapSolversec-ch-ua giống Chromehttp://localhost:7878/fetch để khớp dấu vân tay TLS của ChromeKhái niệm chính: Các client HTTP thông thường thất bại ở bước này ngay cả khi có cookie
cf_clearancehợp lệ — Cloudflare kiểm tra dấu vân tay TLS của chính handshake. Server TLS Go (httpcloak) tạo fetch y hệt Chrome ở mức mạng. Mọi workflow fetch trang được bảo vệ bởi Cloudflare đều phải qua server TLS.### Ví dụ: Trình quét thử thách Cloudflare

Đường dẫn lịch trình:
Mỗi 6 Giờ → Đặt Cấu Hình Mục Tiêu [Lịch trình] → Giải thử thách Cloudflare
→ Chuẩn bị Yêu cầu TLS → Lấy dữ liệu qua TLS Server → Trích xuất Kết quả
Đường dẫn webhook:
Kích hoạt Webhook → Đặt Cấu Hình Mục Tiêu [Webhook] → Giải thử thách Cloudflare
→ Chuẩn bị Yêu cầu TLS → Lấy dữ liệu qua TLS Server → Trích xuất Kết quả → Phản hồi Webhook
targetURL và proxy (theo định dạng host:port:user:pass). Đường dẫn Lịch trình sử dụng giá trị cứng; đường dẫn Webhook đọc từ thân POST.onError: "continueRegularOutput" — tiếp tục ngay cả khi trang hiện tại không hiển thị thử thách.host:port:user:pass thành định dạng URL http://user:pass@host:port, chuyển solution.cookies thành chuỗi header cookie, và xây dựng header yêu cầu giống Chrome với userAgent chính xác lấy từ kết quả giải.http://localhost:7878/fetch sử dụng contentType: "raw" (không phải "json" — chế độ JSON của n8n làm hỏng phần thân).status, body và fetchedAt từ phản hồi server TLS.Tại sao dùng
contentType: "raw"mà không phải"json"? Chế độjsoncủa n8n yêu cầu tham số thân dưới dạng cặp khóa-giá trị. Nếu bạn truyềnJSON.stringify($json)dưới dạng chuỗi, n8n sẽ coi toàn bộ chuỗi là một tham số sai định dạng và gửi{"": ""}đến server. Dùng chế độrawsẽ gửi phần thân chính xác theo kết quả biểu thức.
{
"nodes": [
{
"parameters": {
"content": "## Cloudflare Challenge \u2014 CapSolver + Schedule + Webhook\n\n### How it works\n\n1. Triggers schedule every 6 hours to solve Cloudflare challenges.\n2. Webhook triggers on request to solve Cloudflare challenges.\n3. Sets target and proxy configuration for requests.\n4. Solves the Cloudflare challenge using CapSolver.\n5. Sends results via TLS and processes the HTTP response.\n\n### Setup steps\n\n- Ensure CapSolver credentials are configured.\n- Confirm webhook URL is set and accessible.\n- Check schedule configuration for every 6 hours trigger.\n- Verify endpoint http://localhost:7878/fetch is available.\n\n### Customization\n\nConsider customizing the target URL or proxy settings within the set nodes.",
"width": 480,
"height": 688
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1008,
-112
],
"id": "3c1c3e96-631a-4cf6-bbff-85fd8b1a01e8",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Scheduled Cloudflare challenge\n\nTriggered every 6 hours to solve Cloudflare challenge.",
"width": 1696,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-448,
-112
],
"id": "e9187404-7818-4233-af66-091c90a40476",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Webhook Cloudflare challenge\n\nRespond to solver requests via webhook to solve Cloudflare challenge.",
"width": 2000,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-448,
304
],
"id": "b5d5ba23-8ed2-4afb-8113-3fa8f2fba51e",
"name": "Sticky Note2"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-400,
0
],
"id": "cf111111-1111-1111-1111-111111111101",
"name": "Every 6 Hours"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-s-001",
"name": "targetURL",
"value": "https://www.listaspam.com/busca.php?Telefono=671484239",
"type": "string"
},
{
"id": "cfg-s-002",
"name": "proxy",
"value": "YOUR_PROXY_HOST:PORT:USER:PASS",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-96,
0
],
"id": "cf111111-1111-1111-1111-111111111102",
"name": "Set Target Config [Schedule]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}"
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
208,
0
],
"id": "cf111111-1111-1111-1111-111111111103",
"name": "Solve Cloudflare Challenge [Schedule]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver account"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const config = $('Set Target Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'none',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.targetURL,\n method: 'GET',\n proxy: toProxyURL(config.proxy),\n headers\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
512,
0
],
"id": "cf111111-1111-1111-1111-111111111104",
"name": "Prepare TLS Request [Schedule]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
800,
0
],
"id": "cf111111-1111-1111-1111-111111111105",
"name": "Fetch via TLS Server [Schedule]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "out-s-001",
"name": "status",
"value": "={{ $json.status }}",
"type": "number"
},
{
"id": "out-s-002",
"name": "body",
"value": "={{ $json.body }}",
"type": "string"
},
{
"id": "out-s-003",
"name": "fetchedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1104,
0
],
"id": "cf111111-1111-1111-1111-111111111106",
"name": "Extract Result [Schedule]"
},
{
"parameters": {
"httpMethod": "POST",
"path": "cloudflare-scraper",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-400,
420
],
"id": "cf111111-1111-1111-1111-111111111107",
"name": "Receive Solver Request",
"webhookId": "cf111111-aaaa-bbbb-cccc-111111111107",
"onError": "continueRegularOutput"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-w-001",
"name": "targetURL",
"value": "={{ $json.body.targetURL }}",
"type": "string"
},
{
"id": "cfg-w-002",
"name": "proxy",
"value": "={{ $json.body.proxy }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-96,
420
],
"id": "cf111111-1111-1111-1111-111111111108",
"name": "Set Target Config [Webhook]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}"
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
208,
420
],
"id": "cf111111-1111-1111-1111-111111111109",
"name": "Solve Cloudflare Challenge [Webhook]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver account"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const config = $('Set Target Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'none',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.targetURL,\n method: 'GET',\n proxy: toProxyURL(config.proxy),\n headers\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
512,
420
],
"id": "cf111111-1111-1111-1111-111111111110",
"name": "Prepare TLS Request [Webhook]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
800,
420
],
"id": "cf111111-1111-1111-1111-111111111111",
"name": "Fetch via TLS Server [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "out-w-001",
"name": "status",
"value": "={{ $json.status }}",
"type": "number"
},
{
"id": "out-w-002",
"name": "body",
"value": "={{ $json.body }}",
"type": "string"
},
{
"id": "out-w-003",
"name": "fetchedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1104,
420
],
"id": "cf111111-1111-1111-1111-111111111112",
"name": "Extract Result [Webhook]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1408,
420
],
"id": "cf111111-1111-1111-1111-111111111113",
"name": "Return Solved Token"
}
],
"connections": {
"Every 6 Hours": {
"main": [
[
{
"node": "Set Target Config [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Set Target Config [Schedule]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Schedule]": {
"main": [
[
{
"node": "Prepare TLS Request [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Request [Schedule]": {
"main": [
[
{
"node": "Fetch via TLS Server [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Fetch via TLS Server [Schedule]": {
"main": [
[
{
"node": "Extract Result [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Receive Solver Request": {
"main": [
[
{
"node": "Set Target Config [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Set Target Config [Webhook]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Webhook]": {
"main": [
[
{
"node": "Prepare TLS Request [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Request [Webhook]": {
"main": [
[
{
"node": "Fetch via TLS Server [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Fetch via TLS Server [Webhook]": {
"main": [
[
{
"node": "Extract Result [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Extract Result [Webhook]": {
"main": [
[
{
"node": "Return Solved Token",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
---## Quy trình làm việc: Ví dụ về các trường hợp sử dụng
API Solver và ví dụ về scraper phía trên cho thấy mẫu cốt lõi: giải quyết thử thách, sử dụng giải pháp để truy xuất qua TLS. Các quy trình làm việc sau mở rộng mẫu này cho các trường hợp sử dụng sẵn sàng cho sản xuất — mỗi quy trình đều có các kích hoạt kép (lịch + webhook), theo dõi trạng thái liên tục và kết quả đầu ra có cấu trúc. Mỗi quy trình đều yêu cầu các điều kiện tiên quyết giống nhau: một instance n8n tự lưu trữ, máy chủ TLS chạy trên cổng 7878, một proxy dân cư, và thông tin xác thực CapSolver.
| Quy trình làm việc | Mục đích |
|---|---|
Cloudflare Challenge Scraping — Price & Product Details — CapSolver + Schedule + Webhook |
Cào dữ liệu giá và tên sản phẩm từ trang được bảo vệ bởi CF mỗi 6 giờ, so sánh với các giá trị trước đó, cảnh báo khi có thay đổi |
Cloudflare Challenge Account Login — CapSolver + Schedule + Webhook |
Đăng nhập vào tài khoản của bạn trên trang được bảo vệ bởi CF bằng cách giải thử thách trước, sau đó POST thông tin đăng nhập qua máy chủ TLS |
Turnstile — Solver API |
Cung cấp một webhook giải Turnstile và trả về token — không cần proxy hoặc máy chủ TLS |
Turnstile Scraping — Price & Product Monitor |
Giải Turnstile, lấy trang sản phẩm với token, trích xuất giá và tên sản phẩm, và cảnh báo khi có thay đổi |
Turnstile Account Login |
Đăng nhập vào tài khoản của bạn trên trang được bảo vệ bởi Turnstile bằng cách giải thử thách trước, sau đó POST thông tin đăng nhập cùng token — không cần proxy hoặc máy chủ TLS |
Quy trình làm việc này thu thập dữ liệu trang sản phẩm mỗi 6 giờ (theo lịch) hoặc theo yêu cầu (webhook), trích xuất giá sử dụng nút HTML và so sánh nó với giá trị đã lưu trước đó.
Đường dẫn theo lịch trình:
Mỗi 6 giờ → Thiết lập Cấu hình Mục tiêu → Giải Thách thức CF → Chuẩn bị Yêu cầu TLS
→ Lấy dữ liệu qua Máy chủ TLS → Trích xuất Dữ liệu → So sánh Dữ liệu
→ Dữ liệu thay đổi? → Tạo Cảnh báo / Không thay đổi
Xử lý lỗi: Nếu CapSolver thất bại, quy trình sẽ tiếp tục mà không sử dụng cookie (thông qua
continueOnFail). Việc lấy dữ liệu qua máy chủ TLS vẫn có thể thành công nếu trang hiện không hiển thị thách thức.
Các hành vi chính:
dataPropertyName: "body" (không phải "data") vì máy chủ TLS trả về { status, body, headers }.product-price, h1)$workflow.staticData.lastPrice giữ lại giá trước đó qua các lần chạydeal) và tăng (mức độ nghiêm trọng: info)host:port:user:pass → http://user:pass@host:port qua trợ giúp toProxyURL(){
"nodes": [
{
"parameters": {
"content": "## Thu thập dữ liệu Thách thức Cloudflare — Giá và Thông tin Sản phẩm — CapSolver + Lịch trình + Webhook\n\n### Cách hoạt động\n\n1. Kích hoạt được thiết lập để kiểm tra định kỳ trang web mục tiêu hoặc phản hồi theo webhook bên ngoài.\n2. Áp dụng cấu hình URL mục tiêu và proxy.\n3. Giải quyết các thách thức Cloudflare để vượt qua bảo vệ trang web.\n4. Thực hiện các yêu cầu bảo mật để lấy dữ liệu từ máy chủ mục tiêu.\n5. So sánh dữ liệu trích xuất để kiểm tra sự thay đổi.\n6. Tạo và gửi cảnh báo hoặc trả về dữ liệu dựa trên thay đổi phát hiện.\n\n### Các bước thiết lập\n\n- [ ] Cấu hình URL và cài đặt proxy trong các nút 'Set Target Config'.\n- [ ] Kết nối thông tin xác thực CapSolver để giải thách thức Cloudflare.\n- [ ] Đảm bảo URL webhook được cấu hình đúng để nhận các yêu cầu bên ngoài.\n\n### Tùy chỉnh\n\nĐể điều chỉnh tần suất giám sát, thay đổi khoảng thời gian trong nút 'Every 6 Hours' hoặc cài đặt kích hoạt webhook.",
"width": 480,
"height": 896
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1104,
-192
],
"id": "85c55c3d-335a-47e5-8721-82fa4d633033",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Thiết lập kích hoạt theo lịch trình\n\nKhởi tạo lịch trình chạy mỗi 6 giờ để bắt đầu quá trình thu thập dữ liệu.",
"width": 1392,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-544,
-112
],
"id": "6ac3bd35-66ef-43ed-a8b5-5d5f6d367fba",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Xử lý dữ liệu theo lịch trình\n\nXử lý trích xuất dữ liệu, so sánh và tạo cảnh báo sau khi kích hoạt bởi lịch trình.",
"width": 1088,
"height": 480,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
960,
-192
],
"id": "a1c60513-ed8b-43f2-bff3-a7b19436337f",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## Thiết lập kích hoạt webhook\n\nTheo dõi các yêu cầu bên ngoài qua webhook để bắt đầu quá trình thu thập dữ liệu.",
"width": 1392,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-544,
384
],
"id": "ee4d9a0d-3233-4b70-828b-078c9eee0086",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## Xử lý dữ liệu và phản hồi webhook\n\nTrích xuất và so sánh dữ liệu từ kích hoạt webhook; trả về phản hồi dựa trên sự thay đổi.",
"width": 1392,
"height": 480,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
960,
336
],
"id": "4ad55fbf-2882-4c63-9e09-37e674b00145",
"name": "Sticky Note4"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-500,
0
],
"id": "cf333333-3333-3333-3333-333333333301",
"name": "Every 6 Hours"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-s-001",
"name": "targetURL",
"value": "https://YOUR_CF_PROTECTED_SITE.com/product-page",
"type": "string"
},
{
"id": "cfg-s-002",
"name": "proxy",
"value": "YOUR_PROXY_HOST:PORT:USER:PASS",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-200,
0
],
"id": "cf333333-3333-3333-3333-333333333302",
"name": "Set Target Config [Schedule]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}"
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
100,
0
],
"id": "cf333333-3333-3333-3333-333333333303",
"name": "Solve Cloudflare Challenge [Schedule]",
"onError": "continueRegularOutput",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"jsCode": "const config = $('Set Target Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'none',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.targetURL,\n method: 'GET',\n proxy: toProxyURL(config.proxy),\n headers\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
0
],
"id": "cf333333-3333-3333-3333-333333333304",
"name": "Prepare TLS Request [Schedule]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
700,
0
],
"id": "cf333333-3333-3333-3333-333333333305",
"name": "Fetch via TLS Server [Schedule]"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price",
"returnValue": "text",
"returnArray": false
},
{
"key": "productName",
"cssSelector": "h1, .product-title",
"returnValue": "text",
"returnArray": false
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
1000,
0
],
"id": "cf333333-3333-3333-3333-333333333306",
"name": "Extract Data"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\n\nconst parsePrice = (str) => {\n if (!str) return null;\n const match = str.match(/[\\d]+\\.?\\d*/);\n return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n json: {\n productName,\n currentPrice,\n previousPrice: previousPrice || 'first check',\n changed,\n direction,\n diff: changed ? `$${diff}` : null,\n checkedAt: new Date().toISOString()\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1300,
0
],
"id": "cf333333-3333-3333-3333-333333333307",
"name": "Compare Data"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "price-if-001",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1600,
0
],
"id": "cf333333-3333-3333-3333-333333333308",
"name": "Data Changed?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "alert-001",
"name": "alert",
"value": "=Giá {{ $json.direction }} cho {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }} ({{ $json.direction === 'dropped' ? '-' : '+' }}{{ $json.diff }})",
"type": "string"
},
{
"id": "alert-002",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "alert-003",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1900,
-80
],
"id": "cf333333-3333-3333-3333-333333333309",
"name": "Build Alert"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "nc-001",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "nc-002",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "nc-003",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1900,
128
],
"id": "cf333333-3333-3333-3333-333333333310",
"name": "No Change"
},
{
"parameters": {
"httpMethod": "POST",
"path": "cloudflare-price-monitor",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-500,
500
],
"id": "cf333333-3333-3333-3333-333333333311",
"name": "Receive Monitor Request",
"webhookId": "cf333333-aaaa-bbbb-cccc-333333333311",
"onError": "continueRegularOutput"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-w-001",
"name": "targetURL",
"value": "={{ $json.body.targetURL }}",
"type": "string"
},
{
"id": "cfg-w-002",
"name": "proxy",
"value": "={{ $json.body.proxy }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-200,
500
],
"id": "cf333333-3333-3333-3333-333333333312",
"name": "Set Target Config [Webhook]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}"
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
100,
500
],
"id": "cf333333-3333-3333-3333-333333333313",
"name": "Solve Cloudflare Challenge [Webhook]",
"onError": "continueRegularOutput",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"jsCode": "const config = $('Set Target Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'none',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.targetURL,\n method: 'GET',\n proxy: toProxyURL(config.proxy),\n headers\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
500
],
"id": "cf333333-3333-3333-3333-333333333314",
"name": "Prepare TLS Request [Webhook]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
700,
500
],
"id": "cf333333-3333-3333-3333-333333333315",
"name": "Fetch via TLS Server [Webhook]"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price",
"returnValue": "text",
"returnArray": false
},
{
"key": "productName",
"cssSelector": "h1, .product-title",
"returnValue": "text",
"returnArray": false
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
1008,
528
],
"id": "cf333333-3333-3333-3333-333333333316",
"name": "Extract Data [Webhook]"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\n\nconst parsePrice = (str) => {\n if (!str) return null;\n const match = str.match(/[\\d]+\\.?\\d*/);\n return match ? parseFloat(match[0].replace(',', '')) : null;\n};\n\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\n\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\n\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\n\nreturn [{\n json: {\n productName,\n currentPrice,\n previousPrice: previousPrice || 'first check',\n changed,\n direction,\n diff: changed ? `$${diff}` : null,\n checkedAt: new Date().toISOString()\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1296,
528
],
"id": "cf333333-3333-3333-3333-333333333317",
"name": "Compare Data [Webhook]"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "price-if-002",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1600,
528
],
"id": "cf333333-3333-3333-3333-333333333318",
"name": "Data Changed? [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "alert-004",
"name": "alert",
"value": "=Giá {{ $json.direction }} cho {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }} ({{ $json.direction === 'dropped' ? '-' : '+' }}{{ $json.diff }})",
"type": "string"
},
{
"id": "alert-005",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
},
{
"id": "alert-006",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1904,
448
],
"id": "cf333333-3333-3333-3333-333333333319",
"name": "Build Alert [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "nc-004",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "nc-005",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
},
{
"id": "nc-006",
"name": "checkedAt",
"value": "={{ $json.checkedAt }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1904,
656
],
"id": "cf333333-3333-3333-3333-333333333320",
"name": "No Change [Webhook]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
2208,
528
],
"id": "cf333333-3333-3333-3333-333333333321",
"name": "Return Scraped Data"
}
],
"connections": {
"Every 6 Hours": {
"main": [
[
{
"node": "Set Target Config [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Set Target Config [Schedule]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Schedule]": {
"main": [
[
{
"node": "Prepare TLS Request [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Request [Schedule]": {
"main": [
[
{
"node": "Fetch via TLS Server [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Fetch via TLS Server [Schedule]": {
"main": [
[
{
"node": "Extract Data",
"type": "main",
"index": 0
}
]
]
},
"Extract Data": {
"main": [
[
{
"node": "Compare Data",
"type": "main",
"index": 0
}
]
]
},
"Compare Data": {
"main": [
[
{
"node": "Data Changed?",
"type": "main",
"index": 0
}
]
]
},
"Data Changed?": {
"main": [
[
{
"node": "Build Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change",
"type": "main",
"index": 0
}
]
]
},
"Receive Monitor Request": {
"main": [
[
{
"node": "Set Target Config [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Set Target Config [Webhook]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Webhook]": {
"main": [
[
{
"node": "Prepare TLS Request [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Request [Webhook]": {
"main": [
[
{
"node": "Fetch via TLS Server [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Fetch via TLS Server [Webhook]": {
"main": [
[
{
"node": "Extract Data [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Extract Data [Webhook]": {
"main": [
[
{
"node": "Compare Data [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Compare Data [Webhook]": {
"main": [
[
{
"node": "Data Changed? [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Data Changed? [Webhook]": {
"main": [
[
{
"node": "Build Alert [Webhook]",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Build Alert [Webhook]": {
"main": [
[
{
"node": "Return Scraped Data",
"type": "main",
"index": 0
}
]
]
},
"No Change [Webhook]": {
"main": [
[
{
"node": "Return Scraped Data",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Quy trình làm việc này tự động đăng nhập vào một trang web được bảo vệ bởi Cloudflare. Một nút Set Login Config tập trung tất cả các tham số — [Schedule] cho đường dẫn theo lịch và [Webhook] cho đường dẫn webhook theo yêu cầu.
Đường dẫn theo lịch:
Mỗi 24 giờ → Set Login Config → Giải quyết thử thách CF
→ Chuẩn bị yêu cầu đăng nhập TLS → Gửi đăng nhập qua máy chủ TLS
→ Đăng nhập thành công? → Đánh dấu thành công / Đánh dấu thất bại
Xử lý lỗi: Nếu CapSolver thất bại, quy trình làm việc tiếp tục mà không có cookie (thông qua
continueOnFail). Yêu cầu đăng nhập có khả năng sẽ thất bại, điều này được nút Đăng nhập thành công? phát hiện.
Các hành vi chính:
cf_clearance + userAgent làm header HTTP (không có token trong thân form — khác với đăng nhập reCAPTCHA gửi g-recaptcha-response)URLSearchParams — chỉnh sửa tên trường (usernameField, passwordField) trong Set Login Config cho phù hợp với trang của bạnstatus < 400 VÀ successMarker trong thân phản hồi{
"nodes": [
{
"parameters": {
"content": "## Đăng nhập tài khoản qua thử thách Cloudflare \u2014 CapSolver + Lịch trình + Webhook\n\n### Cách hoạt động\n\n1. Lập lịch quá trình đăng nhập mỗi 24 giờ và giải quyết thử thách Cloudflare.\n2. Chuẩn bị và gửi yêu cầu đăng nhập TLS khi được khởi tạo theo lịch.\n3. Xử lý thành công hoặc thất bại của đăng nhập theo lịch bao gồm ghi lại kết quả.\n4. Nhận yêu cầu đăng nhập qua webhook và giải quyết thử thách Cloudflare.\n5. Chuẩn bị và gửi yêu cầu đăng nhập TLS khi được khởi tạo qua webhook.\n6. Xử lý thành công hoặc thất bại của đăng nhập webhook và trả về kết quả.\n\n### Các bước thiết lập\n\n- [ ] Đảm bảo đã cấu hình thông tin xác thực API CapSolver Cloudflare.\n- [ ] Thiết lập máy chủ TLS chạy cục bộ tại http://localhost:7878.\n- [ ] Cấu hình URL webhook để nhận yêu cầu đăng nhập.\n\n### Tùy chỉnh\n\nĐiều chỉnh khoảng thời gian lịch trình để đáp ứng nhu cầu tần suất cụ thể.",
"width": 480,
"height": 896
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1248,
-320
],
"id": "ba1d6098-8cd2-40f1-b9ae-b945303e5d12",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Bắt đầu đăng nhập theo lịch\n\nBắt đầu quá trình đăng nhập mỗi 24 giờ như một điểm kích hoạt cho các hành động tiếp theo.",
"width": 240,
"height": 336,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-688,
-256
],
"id": "acc8f2a2-298f-4d1b-9db7-8a98fe626abb",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Quy trình đăng nhập theo lịch\n\nXử lý thử thách Cloudflare, chuẩn bị và gửi yêu cầu đăng nhập, và kiểm tra xem đã thành công chưa mỗi 24 giờ.",
"width": 1328,
"height": 480,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-416,
-320
],
"id": "1259d9f4-0b54-4693-89af-193f3ccda6a0",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## Bắt đầu đăng nhập qua webhook\n\nNhận các yêu cầu đăng nhập qua webhook để bắt đầu quy trình tiếp theo.",
"width": 240,
"height": 320,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-688,
272
],
"id": "e95374d2-4029-469d-8e8f-a9afb66ae2ed",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## Quy trình đăng nhập webhook\n\nXử lý các yêu cầu đăng nhập nhận được qua webhook, xử lý thử thách Cloudflare, chuẩn bị và gửi yêu cầu đăng nhập, kiểm tra thành công và ghi lại kết quả.",
"width": 1216,
"height": 528,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-416,
192
],
"id": "da56a87e-6675-439a-8c65-7da631a86df1",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "## Trả lại kết quả webhook\n\nPhản hồi yêu cầu webhook ban đầu với kết quả của lần thử đăng nhập.",
"width": 240,
"height": 320,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
832,
256
],
"id": "7a3f246c-ebd5-419f-a955-b01669743b31",
"name": "Sticky Note5"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 24
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-640,
-80
],
"id": "cf666666-6666-6666-6666-666666666601",
"name": "Mỗi 24 giờ"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-001",
"name": "targetURL",
"value": "https://YOUR_CF_PROTECTED_SITE.com/login",
"type": "string"
},
{
"id": "login-002",
"name": "loginActionURL",
"value": "https://YOUR_CF_PROTECTED_SITE.com/login",
"type": "string"
},
{
"id": "login-003",
"name": "proxy",
"value": "YOUR_PROXY_HOST:PORT:USER:PASS",
"type": "string"
},
{
"id": "login-004",
"name": "usernameField",
"value": "email",
"type": "string"
},
{
"id": "login-005",
"name": "passwordField",
"value": "password",
"type": "string"
},
{
"id": "login-006",
"name": "usernameValue",
"value": "your-email@example.com",
"type": "string"
},
{
"id": "login-007",
"name": "passwordValue",
"value": "YOUR_ACCOUNT_PASSWORD",
"type": "string"
},
{
"id": "login-008",
"name": "successMarker",
"value": "account-dashboard",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-368,
-80
],
"id": "cf666666-6666-6666-6666-666666666602",
"name": "Set Login Config [Schedule]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-144,
-64
],
"id": "cf666666-6666-6666-6666-666666666603",
"name": "Giải quyết thử thách Cloudflare [Schedule]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"jsCode": "const config = $('Set Login Config [Schedule]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst params = new URLSearchParams();\nparams.set(config.usernameField, config.usernameValue);\nparams.set(config.passwordField, config.passwordValue);\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'content-type': 'application/x-www-form-urlencoded',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'same-origin',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.loginActionURL,\n method: 'POST',\n proxy: toProxyURL(config.proxy),\n headers,\n body: params.toString()\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
48,
-64
],
"id": "cf666666-6666-6666-6666-666666666604",
"name": "Chuẩn bị yêu cầu đăng nhập TLS [Schedule]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
240,
-64
],
"id": "cf666666-6666-6666-6666-666666666605",
"name": "Gửi đăng nhập qua máy chủ TLS [Schedule]"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": false,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "login-if-001",
"leftValue": "={{ $json.status < 400 && String($json.body || '').includes($('Set Login Config [Schedule]').item.json.successMarker) }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
432,
-64
],
"id": "cf666666-6666-6666-6666-666666666606",
"name": "Đăng nhập thành công? [Schedule]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-010",
"name": "action",
"value": "account_login",
"type": "string"
},
{
"id": "login-011",
"name": "status",
"value": "success",
"type": "string"
},
{
"id": "login-012",
"name": "message",
"value": "Quy trình đăng nhập tài khoản được cấu hình thành công (bằng cách bỏ qua thử thách Cloudflare)",
"type": "string"
},
{
"id": "login-013",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
768,
-208
],
"id": "cf666666-6666-6666-6666-666666666607",
"name": "Đánh dấu đăng nhập thành công [Schedule]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-014",
"name": "action",
"value": "account_login",
"type": "string"
},
{
"id": "login-015",
"name": "status",
"value": "failed",
"type": "string"
},
{
"id": "login-016",
"name": "statusCode",
"value": "={{ $json.status }}",
"type": "number"
},
{
"id": "login-017",
"name": "message",
"value": "Phản hồi đăng nhập không khớp với dấu hiệu thành công cấu hình",
"type": "string"
},
{
"id": "login-018",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
768,
0
],
"id": "cf666666-6666-6666-6666-666666666608",
"name": "Đánh dấu đăng nhập thất bại [Schedule]"
},
{
"parameters": {
"httpMethod": "POST",
"path": "cloudflare-account-login",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-640,
432
],
"id": "cf666666-6666-6666-6666-666666666609",
"name": "Nhận yêu cầu đăng nhập",
"webhookId": "cf666666-aaaa-bbbb-cccc-666666666609"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-019",
"name": "targetURL",
"value": "={{ $json.body.targetURL }}",
"type": "string"
},
{
"id": "login-020",
"name": "loginActionURL",
"value": "={{ $json.body.loginActionURL }}",
"type": "string"
},
{
"id": "login-021",
"name": "proxy",
"value": "={{ $json.body.proxy }}",
"type": "string"
},
{
"id": "login-022",
"name": "usernameField",
"value": "={{ $json.body.usernameField }}",
"type": "string"
},
{
"id": "login-023",
"name": "passwordField",
"value": "={{ $json.body.passwordField }}",
"type": "string"
},
{
"id": "login-024",
"name": "usernameValue",
"value": "={{ $json.body.usernameValue }}",
"type": "string"
},
{
"id": "login-025",
"name": "passwordValue",
"value": "={{ $json.body.passwordValue }}",
"type": "string"
},
{
"id": "login-026",
"name": "successMarker",
"value": "={{ $json.body.successMarker }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-368,
480
],
"id": "cf666666-6666-6666-6666-666666666610",
"name": "Set Login Config [Webhook]"
},
{
"parameters": {
"operation": "Cloudflare Challenge",
"websiteURL": "={{ $json.targetURL }}",
"proxy": "={{ $json.proxy }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
-144,
480
],
"id": "cf666666-6666-6666-6666-666666666611",
"name": "Giải quyết thử thách Cloudflare [Webhook]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"jsCode": "const config = $('Set Login Config [Webhook]').first().json;\nconst capResult = $input.first().json;\n\nfunction toProxyURL(proxy) {\n if (!proxy) return '';\n if (proxy.startsWith('http')) return proxy;\n const parts = proxy.split(':');\n if (parts.length === 4) {\n return `http://${parts[2]}:${parts[3]}@${parts[0]}:${parts[1]}`;\n }\n return proxy;\n}\n\nlet cookieStr = '';\nlet ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';\n\nif (capResult.data && capResult.data.solution) {\n const solution = capResult.data.solution;\n const cookies = solution.cookies;\n cookieStr = cookies && typeof cookies === 'object'\n ? Object.entries(cookies).map(([k, v]) => `${k}=${v}`).join('; ')\n : (cookies || '');\n if (solution.userAgent) ua = solution.userAgent;\n}\n\nconst chromeMatch = ua.match(/Chrome\\/(\\d+)/);\nconst chromeVer = chromeMatch ? chromeMatch[1] : '145';\nconst secChUa = `\"Chromium\";v=\"${chromeVer}\", \"Not A(Brand\";v=\"8\", \"Google Chrome\";v=\"${chromeVer}\"`;\n\nconst params = new URLSearchParams();\nparams.set(config.usernameField, config.usernameValue);\nparams.set(config.passwordField, config.passwordValue);\n\nconst headers = {\n 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\n 'accept-language': 'en-US,en;q=0.9',\n 'content-type': 'application/x-www-form-urlencoded',\n 'sec-ch-ua': secChUa,\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-platform': '\"Windows\"',\n 'sec-fetch-dest': 'document',\n 'sec-fetch-mode': 'navigate',\n 'sec-fetch-site': 'same-origin',\n 'sec-fetch-user': '?1',\n 'upgrade-insecure-requests': '1',\n 'user-agent': ua\n};\n\nif (cookieStr) headers['cookie'] = cookieStr;\n\nreturn [{ json: {\n url: config.loginActionURL,\n method: 'POST',\n proxy: toProxyURL(config.proxy),\n headers,\n body: params.toString()\n}}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
48,
480
],
"id": "cf666666-6666-6666-6666-666666666612",
"name": "Chuẩn bị yêu cầu đăng nhập TLS [Webhook]"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:7878/fetch",
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
256,
480
],
"id": "cf666666-6666-6666-6666-666666666613",
"name": "Gửi đăng nhập qua máy chủ TLS [Webhook]"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": false,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "login-if-002",
"leftValue": "={{ $json.status < 400 && String($json.body || '').includes($('Set Login Config [Webhook]').item.json.successMarker) }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
448,
496
],
"id": "cf666666-6666-6666-6666-666666666614",
"name": "Đăng nhập thành công? [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-028",
"name": "action",
"value": "account_login",
"type": "string"
},
{
"id": "login-029",
"name": "status",
"value": "success",
"type": "string"
},
{
"id": "login-030",
"name": "message",
"value": "Quy trình đăng nhập tài khoản được cấu hình thành công (bằng cách bỏ qua thử thách Cloudflare)",
"type": "string"
},
{
"id": "login-031",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
640,
320
],
"id": "cf666666-6666-6666-6666-666666666615",
"name": "Đánh dấu đăng nhập thành công [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "login-032",
"name": "action",
"value": "account_login",
"type": "string"
},
{
"id": "login-033",
"name": "status",
"value": "failed",
"type": "string"
},
{
"id": "login-034",
"name": "statusCode",
"value": "={{ $json.status }}",
"type": "number"
},
{
"id": "login-035",
"name": "message",
"value": "Phản hồi đăng nhập không khớp với dấu hiệu thành công cấu hình",
"type": "string"
},
{
"id": "login-036",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
656,
544
],
"id": "cf666666-6666-6666-6666-666666666616",
"name": "Đánh dấu đăng nhập thất bại [Webhook]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
880,
416
],
"id": "cf666666-6666-6666-6666-666666666617",
"name": "Trả lại kết quả đăng nhập"
}
],
"connections": {
"Every 24 Hours": {
"main": [
[
{
"node": "Set Login Config [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Set Login Config [Schedule]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Schedule]": {
"main": [
[
{
"node": "Prepare TLS Login Request [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Login Request [Schedule]": {
"main": [
[
{
"node": "Submit Login via TLS Server [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Submit Login via TLS Server [Schedule]": {
"main": [
[
{
"node": "Login Successful? [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Login Successful? [Schedule]": {
"main": [
[
{
"node": "Mark Login Success [Schedule]",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Login Failed [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Receive Login Request": {
"main": [
[
{
"node": "Set Login Config [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Set Login Config [Webhook]": {
"main": [
[
{
"node": "Solve Cloudflare Challenge [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Solve Cloudflare Challenge [Webhook]": {
"main": [
[
{
"node": "Prepare TLS Login Request [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Prepare TLS Login Request [Webhook]": {
"main": [
[
{
"node": "Submit Login via TLS Server [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Submit Login via TLS Server [Webhook]": {
"main": [
[
{
"node": "Login Successful? [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Login Successful? [Webhook]": {
"main": [
[
{
"node": "Mark Login Success [Webhook]",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Login Failed [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Mark Login Success [Webhook]": {
"main": [
[
{
"node": "Return Login Result",
"type": "main",
"index": 0
}
]
]
},
"Mark Login Failed [Webhook]": {
"main": [
[
{
"node": "Return Login Result",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Workflow này tạo một điểm cuối POST để giải quyết thử thách Cloudflare Turnstile và trả về token. Đây là tương đương với Cloudflare Challenge Solver API ở trên — nhưng đơn giản hơn: không proxy, không máy chủ TLS, chỉ 3 node.
Luồng:
Webhook (POST /solver-turnstile) → Giải Turnstile (CapSolver) → Phản hồi cho Webhook
websiteURL và websiteKeyAntiTurnstileTaskProxyLesstoken)Sự khác biệt chính so với Cloudflare Challenge: Turnstile trả về chuỗi
token, không phải cookiecf_clearance. Bạn gửi token này như headercf-turnstile-response(hoặc trường form, tùy thuộc trang web) trong các yêu cầu tiếp theo. Không cần proxy.
curl -X POST https://your-n8n-instance.com/webhook/solver-turnstile \
-H "Content-Type: application/json" \
-d '{
"websiteURL": "https://target-site.com/page",
"websiteKey": "0x4AAAAAAA..."
}'
Sao chép JSON bên dưới và nhập vào n8n qua Menu → Import from JSON. Sau khi nhập, chọn thông tin xác thực CapSolver của bạn trong node Giải Turnstile.
{
"nodes": [
{
"parameters": {
"content": "## Turnstile \u2014 Solver API\n\n### Cách hoạt động\n\n1. Nhận yêu cầu giải qua webhook.\n2. Giải CAPTCHA Turnstile bằng một node giải chuyên dụng.\n3. Gửi phản hồi lại qua webhook.\n\n### Các bước thiết lập\n\n- [ ] Cấu hình URL webhook để nhận yêu cầu.\n- [ ] Thiết lập thông tin xác thực cho node capSolver.\n- [ ] Đảm bảo URL webhook phản hồi được thiết lập chính xác.\n\n### Tùy chỉnh\n\nCấu hình node giải có thể được điều chỉnh để xử lý các loại CAPTCHA Turnstile khác nhau.",
"width": 480,
"height": 560
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-848,
-80
],
"id": "d52f67cb-cb00-430f-bd76-b74cf4fe6184",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Xử lý yêu cầu giải\n\nNhận và xử lý yêu cầu giải CAPTCHA Turnstile, sau đó gửi kết quả trả về.",
"width": 832,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-288,
-80
],
"id": "21f62617-eaa2-41ae-8fb8-c8502f21c275",
"name": "Sticky Note1"
},
{
"parameters": {
"httpMethod": "POST",
"path": "solver-turnstile",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-240,
32
],
"id": "ts-001",
"name": "Nhận Yêu cầu Giải",
"webhookId": "a7ef0297-8455-44bd-9305-26c179f040b5",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "Cloudflare Turnstile",
"websiteURL": "={{ $json.body.websiteURL }}",
"websiteKey": "={{ $json.body.websiteKey }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
80,
32
],
"id": "ts-002",
"name": "Giải Turnstile",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json.data) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
400,
32
],
"id": "ts-003",
"name": "Phản hồi cho Webhook"
}
],
"connections": {
"Nhận Yêu cầu Giải": {
"main": [
[
{
"node": "Giải Turnstile",
"type": "main",
"index": 0
}
]
]
},
"Giải Turnstile": {
"main": [
[
{
"node": "Phản hồi cho Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Quy trình làm việc này giải quyết Cloudflare Turnstile, lấy trang sản phẩm với token đã giải, trích xuất giá và tên sản phẩm, sau đó so sánh với giá trị trước đó — cảnh báo khi có sự thay đổi. Nó tuân theo mẫu kích hoạt kép (lịch trình + webhook) giống như ví dụ thu thập dữ liệu Cloudflare Challenge ở trên.
Sự khác biệt chính so với thu thập dữ liệu Cloudflare Challenge: Không cần máy chủ TLS, không proxy, không có node mã
Prepare TLS Request. Token Turnstile được gửi trực tiếp dưới dạng headercf-turnstile-responsequa node HTTP Request tích hợp của n8n.
Lộ trình lịch trình:
Mỗi 6 Giờ → Thiết Lập Cấu Hình Mục Tiêu → Giải Turnstile → Lấy Trang Sản Phẩm
→ Trích Xuất Dữ Liệu → So Sánh Dữ Liệu → Dữ Liệu Thay Đổi? → Xây Dựng Cảnh Báo / Không Thay Đổi
Lộ trình webhook:
Kích Hoạt Webhook → Giải Turnstile → Lấy Trang Sản Phẩm
→ Trích Xuất Dữ Liệu → So Sánh Dữ Liệu → Dữ Liệu Thay Đổi? → Xây Dựng Cảnh Báo / Không Thay Đổi
→ Phản Hồi Webhook
Hành vi chính:
Cloudflare Turnstile (không phải Cloudflare Challenge) — sử dụng AntiTurnstileTaskProxyLess bên trongcf-turnstile-response trong yêu cầu lấy trangcf_clearance để so khớp vân tay$workflow.staticData.lastPrice cho việc lưu giữ dữ liệu qua các lần thực thiwebsiteURL và websiteKey trực tiếp từ thân POST (không cần node Set Target Config){
"nodes": [
{
"parameters": {
"content": "## Thu thập dữ liệu Turnstile \u2014 Giám sát Giá & Sản phẩm\n\n### Cách hoạt động\n\n1. Quy trình được kích hoạt mỗi 6 giờ hoặc qua webhook.\n2. Thiết lập cấu hình mục tiêu bao gồm URL trang web và khóa.\n3. Giải captcha Turnstile để truy cập trang sản phẩm.\n4. Lấy dữ liệu sản phẩm và trích xuất dữ liệu liên quan.\n5. So sánh dữ liệu trích xuất với dữ liệu trước đó để phát hiện thay đổi.\n6. Tạo cảnh báo nếu có thay đổi, và gửi phản hồi cho các yêu cầu webhook.\n\n### Các bước thiết lập\n\n- [ ] Cấu hình khoảng thời gian kích hoạt lịch trình theo yêu cầu.\n- [ ] Thiết lập URL webhook để kích hoạt thời gian thực.\n- [ ] Đảm bảo thông tin đăng nhập cho bộ giải captcha là hợp lệ.\n- [ ] Cấu hình URL trang web mục tiêu và khóa sản phẩm.\n\n### Tùy chỉnh\n\nKhoảng thời gian kích hoạt lịch trình có thể điều chỉnh dựa trên nhu cầu giám sát.",
"width": 480,
"height": 896
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1184,
-240
],
"id": "1824aba9-471e-4052-912e-888d939349df",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Kích hoạt thu thập theo lịch trình\n\nKhởi chạy quy trình mỗi 6 giờ và thiết lập cấu hình mục tiêu.",
"width": 512,
"height": 304,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-624,
-80
],
"id": "89b7d6a2-b4bf-4a7d-8667-ce3f31d2eb92",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Giải captcha theo lịch và lấy dữ liệu\n\nGiải captcha và lấy dữ liệu trang sản phẩm theo lịch trình.",
"width": 1472,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
16,
-64
],
"id": "f39fc94b-d0ae-4141-9491-fa82702a72fc",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "## Đánh giá dữ liệu theo lịch trình\n\nSo sánh dữ liệu mới lấy được với dữ liệu trước để phát hiện thay đổi và xây dựng cảnh báo.",
"width": 240,
"height": 528,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1536,
-240
],
"id": "0d31be95-f94c-40fd-99cd-7d988109d3f4",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "## Kích hoạt thu thập qua webhook\n\nKích hoạt quy trình thu thập qua webhook để cập nhật thời gian thực.",
"width": 240,
"height": 352,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-624,
256
],
"id": "e4a50f47-180c-484e-bf70-09a46543e01b",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "## Giải captcha và lấy dữ liệu cho webhook\n\nGiải captcha và lấy dữ liệu trang sản phẩm khi được kích hoạt qua webhook.",
"width": 1472,
"height": 272,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
16,
336
],
"id": "1167cf0e-d561-40fa-b366-331a20d99a31",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "## Đánh giá dữ liệu và phản hồi webhook\n\nĐánh giá thay đổi dữ liệu và phản hồi kết quả cho webhook.",
"width": 672,
"height": 432,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1536,
320
],
"id": "0dab95b9-d762-43cf-9a11-b92212a8c2a7",
"name": "Sticky Note6"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-576,
48
],
"id": "ts-s-01",
"name": "Mỗi 6 Giờ"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cfg-001",
"name": "websiteURL",
"value": "https://YOUR-TARGET-SITE.com/product-page",
"type": "string"
},
{
"id": "cfg-002",
"name": "websiteKey",
"value": "YOUR_SITE_KEY_HERE",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-256,
48
],
"id": "ts-s-02",
"name": "Thiết Lập Cấu Hình Mục Tiêu [Lịch]"
},
{
"parameters": {
"operation": "Cloudflare Turnstile",
"websiteURL": "={{ $json.websiteURL }}",
"websiteKey": "={{ $json.websiteKey }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
64,
48
],
"id": "ts-s-03",
"name": "Giải Turnstile",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"url": "={{ $('Set Target Config [Schedule]').first().json.websiteURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "cf-turnstile-response",
"value": "={{ $json.data.solution.token }}"
}
]
},
"options": {
"response": {
"response": {}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
384,
48
],
"id": "ts-s-04",
"name": "Lấy Trang Sản Phẩm"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price"
},
{
"key": "productName",
"cssSelector": "h1, .product-title"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
704,
48
],
"id": "ts-s-05",
"name": "Trích Xuất Dữ Liệu"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || 'first check', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1072,
48
],
"id": "ts-s-06",
"name": "So Sánh Dữ Liệu"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "if-1",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1344,
48
],
"id": "ts-s-07",
"name": "Dữ Liệu Thay Đổi?"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a1",
"name": "alert",
"value": "=Giá {{ $json.direction }} cho {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
"type": "string"
},
{
"id": "a2",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1584,
-48
],
"id": "ts-s-08",
"name": "Xây Dựng Cảnh Báo"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "n1",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "n2",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1584,
128
],
"id": "ts-s-09",
"name": "Không Thay Đổi"
},
{
"parameters": {
"httpMethod": "POST",
"path": "price-monitor-turnstile",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-576,
448
],
"id": "ts-s-10",
"name": "Kích Hoạt Webhook",
"webhookId": "6a4f76c7-fc5c-440d-96cb-75c9c3bebcdb",
"onError": "continueRegularOutput"
},
{
"parameters": {
"operation": "Cloudflare Turnstile",
"websiteURL": "={{ $json.body.websiteURL }}",
"websiteKey": "={{ $json.body.websiteKey }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
64,
448
],
"id": "ts-s-11",
"name": "Giải Turnstile [W]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "Tài khoản CapSolver"
}
}
},
{
"parameters": {
"url": "={{ $('Webhook Trigger').item.json.body.websiteURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "user-agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
},
{
"name": "cf-turnstile-response",
"value": "={{ $json.data.solution.token }}"
}
]
},
"options": {
"response": {
"response": {}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
384,
448
],
"id": "ts-s-12",
"name": "Lấy Trang Sản Phẩm [W]"
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "price",
"cssSelector": ".product-price, [data-price], .price"
},
{
"key": "productName",
"cssSelector": "h1, .product-title"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [
704,
448
],
"id": "ts-s-13",
"name": "Trích Xuất Dữ Liệu [W]"
},
{
"parameters": {
"jsCode": "const staticData = $workflow.staticData;\nconst currentPrice = $input.first().json.price;\nconst previousPrice = staticData.lastPrice;\nconst productName = $input.first().json.productName || 'Product';\nconst parsePrice = (str) => { if (!str) return null; const match = str.match(/[\\d]+\\.?\\d*/); return match ? parseFloat(match[0].replace(',', '')) : null; };\nconst currentNum = parsePrice(currentPrice);\nconst previousNum = parsePrice(previousPrice);\nstaticData.lastPrice = currentPrice;\nstaticData.lastChecked = new Date().toISOString();\nconst changed = previousNum !== null && currentNum !== null && currentNum !== previousNum;\nconst direction = changed ? (currentNum < previousNum ? 'dropped' : 'increased') : 'unchanged';\nconst diff = changed ? Math.abs(currentNum - previousNum).toFixed(2) : '0';\nreturn [{ json: { productName, currentPrice, previousPrice: previousPrice || 'first check', changed, direction, diff: changed ? `$${diff}` : null, checkedAt: new Date().toISOString() } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1040,
448
],
"id": "ts-s-14",
"name": "So Sánh Dữ Liệu [W]"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "if-2",
"leftValue": "={{ $json.changed }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1344,
448
],
"id": "ts-s-15",
"name": "Dữ Liệu Thay Đổi? [W]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a4",
"name": "alert",
"value": "=Giá {{ $json.direction }} cho {{ $json.productName }}: {{ $json.previousPrice }} \u2192 {{ $json.currentPrice }}",
"type": "string"
},
{
"id": "a5",
"name": "severity",
"value": "={{ $json.direction === 'dropped' ? 'deal' : 'info' }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1584,
432
],
"id": "ts-s-16",
"name": "Xây Dựng Cảnh Báo [W]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "n4",
"name": "status",
"value": "no_change",
"type": "string"
},
{
"id": "n5",
"name": "currentPrice",
"value": "={{ $json.currentPrice }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1584,
592
],
"id": "ts-s-17",
"name": "Không Thay Đổi [W]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
2064,
544
],
"id": "ts-s-18",
"name": "Phản Hồi Webhook"
}
],
"connections": {
"Every 6 Hours": {
"main": [
[
{
"node": "Set Target Config [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Set Target Config [Schedule]": {
"main": [
[
{
"node": "Solve Turnstile",
"type": "main",
"index": 0
}
]
]
},
"Solve Turnstile": {
"main": [
[
{
"node": "Fetch Product Page",
"type": "main",
"index": 0
}
]
]
},
"Fetch Product Page": {
"main": [
[
{
"node": "Extract Data",
"type": "main",
"index": 0
}
]
]
},
"Extract Data": {
"main": [
[
{
"node": "Compare Data",
"type": "main",
"index": 0
}
]
]
},
"Compare Data": {
"main": [
[
{
"node": "Data Changed?",
"type": "main",
"index": 0
}
]
]
},
"Data Changed?": {
"main": [
[
{
"node": "Build Alert",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change",
"type": "main",
"index": 0
}
]
]
},
"Webhook Trigger": {
"main": [
[
{
"node": "Solve Turnstile [W]",
"type": "main",
"index": 0
}
]
]
},
"Solve Turnstile [W]": {
"main": [
[
{
"node": "Fetch Product Page [W]",
"type": "main",
"index": 0
}
]
]
},
"Fetch Product Page [W]": {
"main": [
[
{
"node": "Extract Data [W]",
"type": "main",
"index": 0
}
]
]
},
"Extract Data [W]": {
"main": [
[
{
"node": "Compare Data [W]",
"type": "main",
"index": 0
}
]
]
},
"Compare Data [W]": {
"main": [
[
{
"node": "Data Changed? [W]",
"type": "main",
"index": 0
}
]
]
},
"Data Changed? [W]": {
"main": [
[
{
"node": "Build Alert [W]",
"type": "main",
"index": 0
}
],
[
{
"node": "No Change [W]",
"type": "main",
"index": 0
}
]
]
},
"Build Alert [W]": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"No Change [W]": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Quy trình tự động này thực hiện đăng nhập vào một trang web được bảo vệ bởi Turnstile. Nó theo cùng mẫu kích hoạt kép (lịch trình + webhook) như Đăng nhập tài khoản Cloudflare Challenge phía trên — nhưng không có server TLS, proxy hoặc node mã tùy chỉnh.
Điểm khác biệt chính so với đăng nhập Cloudflare Challenge: Không có server TLS, không có proxy, không có node mã
Prepare TLS Login Request. Token Turnstile được gửi dưới dạng trường biểu mẫucf-turnstile-responsetrực tiếp qua node HTTP Request tích hợp sẵn của n8n. Thông tin đăng nhập được gửi dưới dạng các tham số thân dạngform-urlencodedtiêu chuẩn.
Đường đi theo lịch trình:
Mỗi 24 giờ → Cài đặt cấu hình đăng nhập → Giải Turnstile → Gửi đăng nhập
→ Đăng nhập OK? → Đánh dấu thành công / Đánh dấu thất bại
Đường đi theo webhook:
Kích hoạt webhook → Giải Turnstile → Gửi đăng nhập
→ Đăng nhập OK? → Đánh dấu thành công / Đánh dấu thất bại → Phản hồi webhook
Các hành vi chính:
Cloudflare Turnstile (không phải Cloudflare Challenge) — sử dụng AntiTurnstileTaskProxyLess, không cần proxycf-turnstile-response trong thân POST (không phải header cookie như CF Challenge)email, password, và cf-turnstile-responsestatusCode < 400 VÀ successMarker trong thân phản hồi — cùng mẫu với đăng nhập CF ChallengeusernameValue, passwordValue, usernameField, passwordField, loginActionURL, successMarker từ thân POSTname của mỗi trường biểu mẫu là một biểu thức ($('Webhook Trigger').item.json.body.usernameField || 'email'), vì vậy người gọi có thể chỉ định tên trường của trang web họ{
"nodes": [
{
"parameters": {
"content": "## Đăng nhập tài khoản Turnstile\n\n### Cách hoạt động\n\n1. Kích hoạt quy trình đăng nhập mỗi 24 giờ thông qua lịch trình.\n2. Cài đặt cấu hình đăng nhập và giải các thử thách Turnstile.\n3. Gửi biểu mẫu đăng nhập và kiểm tra xem đăng nhập có thành công không.\n4. Đánh dấu lần đăng nhập đó là thành công hoặc thất bại trong luồng lịch trình.\n5. Ngoài ra, bắt đầu quy trình đăng nhập qua kích hoạt webhook và thực hiện các bước tương tự cho đăng nhập dựa trên webhook.\n\n### Các bước cài đặt\n\n- [ ] Lên lịch chạy định kỳ bằng cách thiết lập bộ lập lịch.\n- [ ] Cấu hình endpoint webhook để kích hoạt bên ngoài.\n- [ ] Đặt thông tin đăng nhập và cấu hình URL.\n\n### Tuỳ chỉnh\n\nĐiều chỉnh thời gian trong bộ lập lịch hoặc sửa xử lý phản hồi webhook nếu cần.",
"width": 480,
"height": 896
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1168,
-160
],
"id": "8ca80c21-2de5-41e7-b3e4-de184fc1d8fe",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## Luồng đăng nhập theo lịch trình\n\nKích hoạt mỗi 24 giờ để thực hiện các bước đăng nhập",
"width": 1920,
"height": 448,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-608,
-160
],
"id": "533be07a-6fe7-4ada-a40c-7a7749ba968d",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "## Luồng đăng nhập webhook\n\nXử lý các yêu cầu đăng nhập được kích hoạt bởi webhook",
"width": 2288,
"height": 416,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-608,
320
],
"id": "c6a8e296-2bfb-4ffa-8819-aa420e936589",
"name": "Sticky Note2"
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 24
}
]
}
},
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-560,
48
],
"id": "ts-l-01",
"name": "Every 24 Hours"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "l1",
"name": "websiteURL",
"value": "https://YOUR-LOGIN-PAGE.com",
"type": "string"
},
{
"id": "l2",
"name": "websiteKey",
"value": "YOUR_SITE_KEY_HERE",
"type": "string"
},
{
"id": "l3",
"name": "successMarker",
"value": "account-dashboard",
"type": "string"
},
{
"id": "l4",
"name": "userAgent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-240,
48
],
"id": "ts-l-02",
"name": "Set Login Config [Schedule]"
},
{
"parameters": {
"operation": "Cloudflare Turnstile",
"websiteURL": "={{ $json.websiteURL }}",
"websiteKey": "={{ $json.websiteKey }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
256,
48
],
"id": "ts-l-03",
"name": "Solve Turnstile [Schedule]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Set Login Config [Schedule]').item.json.websiteURL }}/login",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "content-type",
"value": "application/x-www-form-urlencoded"
},
{
"name": "user-agent",
"value": "={{ $('Set Login Config [Schedule]').item.json.userAgent }}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "email",
"value": "your-email@example.com"
},
{
"name": "password",
"value": "YOUR_ACCOUNT_PASSWORD"
},
{
"name": "cf-turnstile-response",
"value": "={{ $json.data.solution.token }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
576,
48
],
"id": "ts-l-04",
"name": "Submit Login [Schedule]"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "strict"
},
"conditions": [
{
"id": "lif1",
"leftValue": "={{ $json.statusCode < 400 && String($json.body || $json.data || '').includes($('Set Login Config [Schedule]').item.json.successMarker) }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
896,
48
],
"id": "ts-l-05",
"name": "Login OK? [Schedule]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "s1",
"name": "status",
"value": "success",
"type": "string"
},
{
"id": "s2",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1168,
-48
],
"id": "ts-l-06",
"name": "Mark Success"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "f1",
"name": "status",
"value": "failed",
"type": "string"
},
{
"id": "f2",
"name": "statusCode",
"value": "={{ $json.statusCode }}",
"type": "number"
},
{
"id": "f3",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1168,
128
],
"id": "ts-l-07",
"name": "Mark Failed"
},
{
"parameters": {
"httpMethod": "POST",
"path": "account-login-turnstile",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2.1,
"position": [
-560,
544
],
"id": "ts-l-08",
"name": "Webhook Trigger",
"webhookId": "9c7a53a4-d3ee-495b-9381-3a9425bb1b36"
},
{
"parameters": {
"operation": "Cloudflare Turnstile",
"websiteURL": "={{ $json.body.websiteURL }}",
"websiteKey": "={{ $json.body.websiteKey }}",
"optional": {}
},
"type": "n8n-nodes-capsolver.capSolver",
"typeVersion": 1,
"position": [
256,
544
],
"id": "ts-l-09",
"name": "Solve Turnstile [Webhook]",
"credentials": {
"capSolverApi": {
"id": "BeBFMAsySMsMGeE9",
"name": "CapSolver account"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook Trigger').item.json.body.loginActionURL }}",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "content-type",
"value": "application/x-www-form-urlencoded"
},
{
"name": "user-agent",
"value": "={{ $('Webhook Trigger').item.json.body.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36' }}"
}
]
},
"sendBody": true,
"contentType": "form-urlencoded",
"bodyParameters": {
"parameters": [
{
"name": "={{ $('Webhook Trigger').item.json.body.usernameField || 'email' }}",
"value": "={{ $('Webhook Trigger').item.json.body.usernameValue }}"
},
{
"name": "={{ $('Webhook Trigger').item.json.body.passwordField || 'password' }}",
"value": "={{ $('Webhook Trigger').item.json.body.passwordValue }}"
},
{
"name": "cf-turnstile-response",
"value": "={{ $json.data.solution.token }}"
}
]
},
"options": {
"response": {
"response": {
"fullResponse": true,
"neverError": true
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
576,
544
],
"id": "ts-l-10",
"name": "Submit Login [Webhook]"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "strict"
},
"conditions": [
{
"id": "lif2",
"leftValue": "={{ $json.statusCode < 400 && String($json.body || $json.data || '').includes($('Webhook Trigger').item.json.body.successMarker) }}",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
896,
544
],
"id": "ts-l-11",
"name": "Login OK? [Webhook]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ws1",
"name": "status",
"value": "success",
"type": "string"
},
{
"id": "ws2",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1168,
432
],
"id": "ts-l-12",
"name": "Mark Success [W]"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "wf1",
"name": "status",
"value": "failed",
"type": "string"
},
{
"id": "wf2",
"name": "statusCode",
"value": "={{ $json.statusCode }}",
"type": "number"
},
{
"id": "wf3",
"name": "checkedAt",
"value": "={{ new Date().toISOString() }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1168,
576
],
"id": "ts-l-13",
"name": "Mark Failed [W]"
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify($json) }}",
"options": {}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.5,
"position": [
1536,
544
],
"id": "ts-l-14",
"name": "Respond to Webhook"
}
],
"connections": {
"Every 24 Hours": {
"main": [
[
{
"node": "Set Login Config [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Set Login Config [Schedule]": {
"main": [
[
{
"node": "Solve Turnstile [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Solve Turnstile [Schedule]": {
"main": [
[
{
"node": "Submit Login [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Submit Login [Schedule]": {
"main": [
[
{
"node": "Login OK? [Schedule]",
"type": "main",
"index": 0
}
]
]
},
"Login OK? [Schedule]": {
"main": [
[
{
"node": "Mark Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Failed",
"type": "main",
"index": 0
}
]
]
},
"Webhook Trigger": {
"main": [
[
{
"node": "Solve Turnstile [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Solve Turnstile [Webhook]": {
"main": [
[
{
"node": "Submit Login [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Submit Login [Webhook]": {
"main": [
[
{
"node": "Login OK? [Webhook]",
"type": "main",
"index": 0
}
]
]
},
"Login OK? [Webhook]": {
"main": [
[
{
"node": "Mark Success [W]",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Failed [W]",
"type": "main",
"index": 0
}
]
]
},
"Mark Success [W]": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Mark Failed [W]": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"meta": {
"instanceId": "962ff0267b713be0344b866fa54daae28de8ed2144e2e6867da355dae193ea1f"
}
}
Bạn đã xây dựng một pipeline vượt qua thử thách Cloudflare hoàn chỉnh trong n8n — không dùng tự động hóa trình duyệt, không Puppeteer, không Playwright. Chỉ có ba thành phần hoạt động cùng nhau: CapSolver để giải thử thách, một server TLS Go để giả mạo dấu vân tay mạng của Chrome, và một workflow n8n để điều phối mọi thứ.
Điểm mấu chốt là giải được thử thách chỉ mới là một nửa vấn đề. Nếu không khớp dấu vân tay TLS trong lần truy xuất tiếp theo, cf_clearance trở nên vô dụng — Cloudflare kiểm tra cả handshake, không chỉ cookie. Server TLS httpcloak chịu trách nhiệm lớp này, giúp lần truy xuất không thể phân biệt được với trình duyệt Chrome thật ở cấp độ mạng.
Repository giờ đây cung cấp cho bạn các mẫu khởi đầu thực tế cho các trang web được bảo vệ bởi Cloudflare:
cf_clearance thô + userAgent cho các ứng dụng ngoàiSolver API là điểm vào đơn giản nhất — 4 node, không cần server TLS. Các workflow Turnstile còn đơn giản hơn — không cần proxy hay server TLS chút nào, vì Turnstile trả về token (chứ không phải cookie ràng buộc IP) và không tạo dấu vân tay TLS. Đối với workflow Cloudflare Challenge mà truy xuất trang trực tiếp, CapSolver giải thử thách và server TLS thực hiện yêu cầu thực tế. Cấu hình các chỗ giữ chỗ, giữ workflow ở trạng thái không hoạt động cho đến khi chúng phù hợp với mục tiêu của bạn, rồi kích hoạt.
Mẹo: Các workflow này sử dụng triggers Schedule + Webhook, nhưng bạn có thể thay node trigger bằng bất kỳ trigger nào của n8n — thủ công, sự kiện ứng dụng, gửi form, v.v. Sau khi lấy dữ liệu, dùng các node tích hợp sẵn của n8n để lưu kết quả vào Google Sheets, cơ sở dữ liệu, lưu trữ đám mây hoặc gửi cảnh báo qua Telegram/Slack/Email.
Sẵn sàng bắt đầu? Đăng ký CapSolver và dùng mã bonus n8n để nhận thêm 8% tiền thưởng cho lần nạp đầu tiên!

AntiCloudflareTask giải thử thách quản lý Bot Cloudflare toàn trang — màn hình "Just a moment…" chặn hoàn toàn truy cập trang. Nó cần proxy vì CapSolver phải tải trang được bảo vệ thực sự qua trình duyệt. AntiTurnstileTaskProxyLess giải các widget Turnstile nhúng trong trang (form đăng nhập, form đăng ký) và không cần proxy. Thử thách khác nhau, loại task khác nhau.
Node HTTP Request của n8n dùng thư viện net/http chuẩn của Go, có dấu vân tay TLS riêng biệt mà Cloudflare phát hiện được. Dù có cookie cf_clearance hợp lệ, Cloudflare sẽ thách thức lại bất kỳ yêu cầu nào không khớp profile TLS trình duyệt đã biết. Server TLS giải quyết bằng cách dùng httpcloak để giả mạo stack TLS Chrome thật.
Điểm đánh giá bot của Cloudflare gán điểm rủi ro cho các địa chỉ IP. IP datacenter (từ AWS, GCP, các nhà cung cấp VPS, v.v.) đã rất nổi tiếng và có điểm rủi ro cao. AntiCloudflareTask dùng proxy của bạn để tải trang thử thách, nếu Cloudflare nhận diện IP là datacenter, nó sẽ hoặc cấp thử thách khó hơn mà CapSolver không thể giải, hoặc cho thách thức không thành công luôn. IP residential và di động có điểm rủi ro thấp hơn và qua dễ hơn.
Không cần. Turnstile hoàn toàn khác với Cloudflare Challenge. Turnstile trả về một token ngắn hạn bạn gửi kèm header hoặc trường form — không liên kết với IP hay dấu vân tay TLS nào. CapSolver giải Turnstile bằng AntiTurnstileTaskProxyLess, không cần proxy. Và vì bạn gửi một token (chứ không phải cookie ràng buộc IP), node HTTP Request tích hợp sẵn của n8n hoạt động tốt — không cần giả mạo dấu vân tay TLS.
cf_clearance có hết hạn không?Có. Thời gian hết hạn phụ thuộc vào cấu hình Cloudflare của trang — có thể từ vài phút đến 24 giờ. Với các công việc scraping định kỳ, workflow theo lịch (6 giờ/lần) sẽ giải thử thách định kỳ. Với scraping theo yêu cầu, đường dẫn webhook sẽ giải thử thách mới cho mỗi lần gọi.
Học kiến trúc gỡ mã web Rust có thể mở rộng với reqwest, scraper, gỡ mã bất đồng bộ, gỡ mã trình duyệt không đầu, xoay proxy và xử lý CAPTCHA tuân thủ.

Tự động hóa việc giải CAPTCHA với Nanobot và CapSolver. Sử dụng Playwright để giải reCAPTCHA và Cloudflare tự động.

Hiểu về Dịch vụ Dữ liệu (DaaS) vào năm 2026. Khám phá lợi ích, trường hợp sử dụng và cách nó thay đổi doanh nghiệp với phân tích thời gian thực và tính mở rộng.

Nắm vững việc sửa chữa các lỗi trình gỡ mã web đa dạng như 400, 401, 402, 403, 429, 5xx, và Cloudflare 1001 vào năm 2026. Học các chiến lược tiên tiến về chuyển đổi IP, tiêu đề, và giới hạn tốc độ thích ứng với CapSolver.
