Sản phẩmTích hợpTài nguyênTài liệuGiá cả
Bắt đầu ngay

© 2026 CapSolver. All rights reserved.

Liên hệ chúng tôi

Slack: lola@capsolver.com

Sản phẩm

  • reCAPTCHA v2
  • reCAPTCHA v3
  • Cloudflare Turnstile
  • Cloudflare Challenge
  • AWS WAF
  • Tiện ích trình duyệt
  • Thêm nhiều loại CAPTCHA

Tích hợp

  • Selenium
  • Playwright
  • Puppeteer
  • n8n
  • Đối tác
  • Xem tất cả tích hợp

Tài nguyên

  • Chương trình giới thiệu
  • Tài liệu
  • Tham chiếu API
  • Blog
  • Câu hỏi thường gặp
  • Thuật ngữ
  • Trạng thái

Pháp lý

  • Điều khoản dịch vụ
  • Chính sách bảo mật
  • Chính sách hoàn tiền
  • Không bán thông tin cá nhân của tôi
//Cách giải Cloudflare Challenge trong n8n với CapSolver
Mar10, 2026

Cách giải Cloudflare Challenge trong n8n với CapSolver

Anh Tuan

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:

  • Một workflow kích hoạt kép (lịch trình + webhook) vượt qua Cloudflare Bot Management
  • Một server Go nhẹ dùng để gửi yêu cầu HTTP với dấu vân tay TLS Chrome thật
  • Một mẫu trình thu thập dữ liệu có thể tái sử dụng bạn có thể trỏ tới bất kỳ site được bảo vệ CF nào
  • Các workflow dùng trong thu thập dữ liệu, đăng nhập tài khoản, và một API giải thử thách độc lập — tất cả đều nằm sau Cloudflare Challenge
  • Các workflow Turnstile — một API giải, một mẫu thu thập dữ liệu, và một luồng đăng nhập tài khoản cho các site dùng Cloudflare Turnstile (không cần proxy hay server TLS)

Cloudflare Challenge là gì?

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.


Tại sao các client HTTP tiêu chuẩn thất bại

Đâ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:

  • Dấu vân tay JA3 / JA4
  • Các frame HTTP/2 SETTINGS
  • Thỏa thuận ALPN
  • Thứ tự và giá trị header

Đ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

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.

Thiết lập CapSolver trong n8n

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.

Bước 1: Mở trang Credentials

Đi tới instance n8n của bạn và chọn Settings → Credentials.

Trang credentials n8n hiển thị tài khoản CapSolver

Bước 2: Tạo Credential CapSolver

  1. Nhấn Create credential (góc trên bên phải)
  2. Tìm kiếm "CapSolver" và chọn CapSolver API
  3. Nhập API Key từ Bảng điều khiển CapSolver
  4. Để Allowed HTTP Request Domains là All
  5. Nhấn Save

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

Cấu hình credential CapSolver với kiểm tra kết nối thành công

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.


Bước 1 — Xây dựng server TLS

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 file nguồn

Tạo thư mục và lưu file dưới tên main.go:

bash Copy
mkdir -p ~/tls-server && cd ~/tls-server
go Copy
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))
}

Khởi tạo và build

bash Copy
go mod init tls-server
go get github.com/sardanioss/httpcloak/client
go build -o main main.go

Chạy server

bash Copy
./main

Kiểm tra server đang chạy (trong terminal mới)

bash Copy
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.


Bước 2 — Cho phép n8n gọi localhost

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:

bash Copy
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: API giải Cloudflare Challenge

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.

Cách hoạt động

Copy
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.

  1. Webhook — Nhận yêu cầu POST với websiteURL và proxy
  2. Cloudflare Challenge — CapSolver giải thử thách dùng AntiCloudflareTask
  3. Định dạng giải pháp — Chuẩn hóa đối tượng cookies thành chuỗi cookie, xử lý lỗi với continueOnFail
  4. Trả lời Webhook — Trả về cf_clearance, chuỗi cookie đã chuẩn hóa, và userAgent### Cấu hình Node

1. Node Webhook

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

2. Node CapSolver (Thử thách Cloudflare)

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 clearance
  • userAgent — chuỗi User-Agent chính xác mà trình duyệt đã sử dụng trong quá trình giải

Lưu ý: Trường là websiteURL (không phải targetURL) — 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.

3. Định dạng Kết quả (Node Code)

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.

javascript Copy
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()
}}];

4. Phản hồi Webhook

Cài đặt Giá trị
Respond With JSON
Response Body ={{ JSON.stringify($json) }}

Thử nghiệm

bash Copy
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:

json Copy
{
  "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.):

json Copy
{
  "success": false,
  "error": "No solution returned — site may not be showing a challenge"
}

Nhập Workflow này

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.

Nhấn để mở rộng JSON workflow
json Copy
{
  "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"
  }
}

Workflow: Sử dụng các giải pháp Thử thách Cloudflare

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:

  1. Giải Thử thách Cloudflare → Lấy cookie cf_clearance và userAgent từ CapSolver
  2. Chuẩn bị yêu cầu → Xây dựng header với cookie, User-Agent khớp và các header sec-ch-ua giống Chrome
  3. Fetch qua server TLS → Gửi yêu cầu qua http://localhost:7878/fetch để khớp dấu vân tay TLS của Chrome
  4. Xác minh phản hồi → Kiểm tra xem trang có trả nội dung thật (status 200 với HTML) hay là trang thử thách lại
  5. Xử lý kết quả → Trích xuất dữ liệu bạn cần bằng node HTML, Edit Fields hoặc Code node

Khá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_clearance hợ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

Quy trình thử thách Cloudflare trong n8n với đường dẫn lịch trình và webhook

Luồng workflow

Đường dẫn lịch trình:

Copy
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:

Copy
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

Cách thức hoạt động

  1. Đặt Cấu Hình Mục Tiêu — Lưu 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.
  2. Giải thử thách Cloudflare — Node CapSolver với onError: "continueRegularOutput" — tiếp tục ngay cả khi trang hiện tại không hiển thị thử thách.
  3. Chuẩn bị Yêu cầu TLS — Node code chuyển proxy 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.
  4. Lấy dữ liệu qua TLS Server — Gửi yêu cầu HTTP tớ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).
  5. Trích xuất Kết quả — Lấy status, body và fetchedAt từ phản hồi server TLS.
  6. Phản hồi Webhook — Trả về kết quả dưới dạng JSON (chỉ đường dẫn webhook).

Tại sao dùng contentType: "raw" mà không phải "json"? Chế độ json củ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ền JSON.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ế độ raw sẽ gửi phần thân chính xác theo kết quả biểu thức.

Nhấn để xem JSON workflow
json Copy
{
  "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:

Copy
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:

  • Sử dụng dataPropertyName: "body" (không phải "data") vì máy chủ TLS trả về { status, body, headers }
  • Nút HTML trích xuất giá và tên sản phẩm qua bộ chọn CSS (.product-price, h1)
  • $workflow.staticData.lastPrice giữ lại giá trước đó qua các lần chạy
  • So sánh giá phát hiện cả giảm (mức độ nghiêm trọng: deal) và tăng (mức độ nghiêm trọng: info)
  • Tự động chuyển đổi định dạng proxy: host:port:user:pass → http://user:pass@host:port qua trợ giúp toProxyURL()
Nhấn để mở rộng JSON đầy đủ của quy trình làm việc
json Copy
{
  "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"
  }
}
### Ví dụ 2: Đăng nhập tài khoản

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:

Copy
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:

  • Sử dụng cookie 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)
  • Trường form sử dụng 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ạn
  • Đăng nhập thành công? kiểm tra status < 400 VÀ successMarker trong thân phản hồi
  • Đường dẫn webhook trả về kết quả dưới dạng JSON qua Respond to Webhook
Nhấn để xem đầy đủ JSON của quy trình làm việc
json Copy
{
  "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"
  }
}
### Ví dụ 3: Turnstile — Solver API

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:

Copy
Webhook (POST /solver-turnstile) → Giải Turnstile (CapSolver) → Phản hồi cho Webhook
  1. Webhook — Nhận yêu cầu POST với websiteURL và websiteKey
  2. Giải Turnstile — CapSolver giải thử thách Turnstile sử dụng AntiTurnstileTaskProxyLess
  3. Phản hồi cho Webhook — Trả về dữ liệu giải pháp (bao gồm token)

Sự khác biệt chính so với Cloudflare Challenge: Turnstile trả về chuỗi token, không phải cookie cf_clearance. Bạn gửi token này như header cf-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.

Thử nghiệm

bash Copy
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..."
  }'

Nhập Workflow này

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.

Nhấp để mở rộng JSON của workflow
json Copy
{
  "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"
  }
}
### Ví dụ 4: Thu thập dữ liệu Turnstile — Giám sát Giá & Sản phẩm

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 header cf-turnstile-response qua node HTTP Request tích hợp của n8n.

Lộ trình lịch trình:

Copy
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:

Copy
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:

  • Hoạt động CapSolver là Cloudflare Turnstile (không phải Cloudflare Challenge) — sử dụng AntiTurnstileTaskProxyLess bên trong
  • Token được gửi như một header cf-turnstile-response trong yêu cầu lấy trang
  • Không cần chuyển đổi định dạng proxy — giải Turnstile không sử dụng proxy
  • Node HTTP Request tiêu chuẩn của n8n — không cần máy chủ TLS vì không có cookie cf_clearance để so khớp vân tay
  • Logic so sánh giá giống như phiên bản Challenge: $workflow.staticData.lastPrice cho việc lưu giữ dữ liệu qua các lần thực thi
  • Lộ trình webhook đọc websiteURL và websiteKey trực tiếp từ thân POST (không cần node Set Target Config)
Nhấn để mở rộng JSON workflow
json Copy
{
  "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"
  }
}
### Ví dụ 5: Đăng nhập tài khoản Turnstile

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ẫu cf-turnstile-response trự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ạng form-urlencoded tiêu chuẩn.

Đường đi theo lịch trình:

Copy
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:

Copy
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:

  • Hoạt động CapSolver là Cloudflare Turnstile (không phải Cloudflare Challenge) — sử dụng AntiTurnstileTaskProxyLess, không cần proxy
  • Token được gửi dưới dạng trường biểu mẫu cf-turnstile-response trong thân POST (không phải header cookie như CF Challenge)
  • Không cần node mã tùy chỉnh — node HTTP Request trực tiếp xử lý gửi biểu mẫu dạng form-urlencoded với các trường email, password, và cf-turnstile-response
  • Đăng nhập OK? kiểm tra statusCode < 400 VÀ successMarker trong thân phản hồi — cùng mẫu với đăng nhập CF Challenge
  • Đường đi lịch trình dùng thông tin đăng nhập được mã hóa cứng trong Set Login Config; Đường đi webhook đọc usernameValue, passwordValue, usernameField, passwordField, loginActionURL, successMarker từ thân POST
  • Đường đi webhook hỗ trợ tên trường động — tham số name 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ọ
Nhấp để mở rộng JSON quy trình làm việc
json Copy
{
  "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"
  }
}
## Kết luận

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:

  • Challenge Solver API — cho phép một webhook trả về cf_clearance thô + userAgent cho các ứng dụng ngoài
  • Scraping — trích xuất dữ liệu giá và sản phẩm từ các trang được bảo vệ bởi Cloudflare, có phát hiện thay đổi
  • Account login — đăng nhập vào tài khoản của bạn trên các trang Cloudflare bảo vệ
  • Turnstile Solver API — webhook 3 node trả về token Turnstile (không cần proxy, không cần server TLS)
  • Turnstile Scraping — giám sát giá với các trang được bảo vệ Turnstile dùng node HTTP Request tích hợp sẵn của n8n
  • Turnstile Account Login — đăng nhập vào các trang được bảo vệ Turnstile với thông tin đăng nhập dạng form-urlencoded và token đã được giải

Solver 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!

Biểu ngữ mã bonus CapSolver

Câu hỏi thường gặp

AntiCloudflareTask là gì, và nó khác gì so với AntiTurnstileTaskProxyLess?

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.

Tại sao tôi không thể chỉ dùng node HTTP Request tích hợp sẵn của n8n để tải trang?

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.

Tại sao proxy datacenter của tôi lại thường xuyên thất bại?

Đ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.

Các workflow Turnstile có cần server TLS hoặc proxy không?

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.

Nội dung

Xem thêm

Web ScrapingApr 22, 2026

Kiến trúc Trích xuất Dữ liệu Từ Web bằng Rust cho Trích xuất Dữ liệu Có Thể Mở Rộng

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ủ.

Anh Tuan
Anh Tuan
Web ScrapingFeb 17, 2026

Cách giải CAPTCHA trên Nanobot bằng CapSolver

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.

Anh Tuan
Anh Tuan
Web ScrapingFeb 10, 2026

Dữ liệu dưới dạng dịch vụ (DaaS): Nó là gì và tại sao nó quan trọng vào năm 2026

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.

Emma Foster
Emma Foster
Web ScrapingFeb 05, 2026

Cách sửa các lỗi thu thập dữ liệu web phổ biến vào năm 2026

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.

Nikolai Smirnov
Nikolai Smirnov
Blog
Web Scraping