Cloudflare Turnstile có ba chế độ widget: managed (Cloudflare tự quyết định mức thử thách), non-interactive (chỉ chạy proof-of-work, không bao giờ hiện giao diện) và invisible (không có container nào xuất hiện trên trang). Nếu script tự động của bạn nhận token cf-turnstile-response lúc thì ngay lập tức, lúc thì phải đợi vài giây với một spinner, nhiều khả năng bạn đang gặp hai chế độ khác nhau chứ không phải một lỗi ngẫu nhiên.
Tin tốt cho việc tích hợp: cả ba chế độ trả về cùng một token, và CaptchaAI giải cả ba bằng đúng một method API — turnstile.
Nhận diện chế độ trong 3 bước
Trước khi đọc chi tiết từng chế độ, đây là cách xác định nhanh site đang chạy gì chỉ bằng cách đọc HTML:
- Có
data-size="invisible"(hoặcsize: 'invisible'trong JS) → invisible mode, không có container hiển thị. - Không có
data-size="invisible"nhưng códata-appearance="interaction-only"→ non-interactive mode, chỉ hiện spinner, không có checkbox. - Không có cả hai thuộc tính trên → managed mode (mặc định), widget có thể hiện checkbox tùy tín hiệu tin cậy.
Cả ba trường hợp đều dùng chung một class cf-turnstile và trả về cùng một token — bước phân loại này chỉ quyết định bạn cần đợi UI hay không, không đổi cách gọi API giải.
Mẹo debug nhanh: chạy
document.querySelectorAll('.cf-turnstile')trong console sau khi trang load xong hẳn — lộ ra container thật kể cả ở invisible mode.
Bảng so sánh nhanh 3 chế độ widget Turnstile
| Tiêu chí | Managed | Non-interactive | Invisible |
|---|---|---|---|
| Widget có hiển thị không? | Đôi khi (checkbox) | Không, chỉ có spinner | Không, hoàn toàn ẩn |
| Cần phần tử container? | Có | Có | Có (ẩn) |
| Cần người dùng tương tác? | Đôi khi | Không | Không |
| Chạy proof-of-work? | Có, có thể leo thang độ khó | Có, luôn luôn | Có, luôn luôn |
| Fallback về checkbox khi lỗi? | Có | Không, báo lỗi luôn | Không, báo lỗi luôn |
| Token trả về | cf-turnstile-response |
cf-turnstile-response |
cf-turnstile-response |
| Method CaptchaAI | turnstile |
turnstile |
turnstile |
| Phù hợp với | Đăng nhập, đăng ký | Form ma sát thấp (newsletter, comment) | Xác minh nền, không hiện UI |
Chọn chế độ nào phù hợp sản phẩm của bạn
Không có chế độ "tốt nhất" tuyệt đối — chọn theo mức rủi ro chấp nhận đánh đổi:
| Ưu tiên | Chế độ nên dùng |
|---|---|
| Bảo mật cao nhất cho đăng nhập, đăng ký | Managed |
| Ma sát tối thiểu cho form giá trị thấp | Non-interactive |
| Không hiện UI cho người dùng | Invisible |
Hai lưu ý trước khi đổi cấu hình production:
- Non-interactive không có fallback — trình duyệt cũ có thể bị chặn hẳn thay vì thấy checkbox.
- Luôn test cả ba chế độ trên staging trước, vì hành vi hiển thị phụ thuộc tín hiệu trình duyệt thực tế.
Managed mode: mặc định của Cloudflare Turnstile
Managed để Cloudflare tự quyết định mức thử thách cho từng khách truy cập.
Phần lớn người dùng vượt qua mà không thấy gì cả. Traffic bị nghi ngờ sẽ thấy checkbox, traffic nghi ngờ cao hơn có thể gặp thử thách phức tạp hơn.
Cách triển khai
<!-- Managed mode (default) -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-theme="light">
</div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
Widget phản ứng thế nào theo mức độ tin cậy
Managed điều chỉnh dựa trên tín hiệu của người truy cập:
- Độ tin cậy cao — pass ẩn, không có UI nào hiện ra.
- Độ tin cậy trung bình — hiện checkbox, người dùng bấm để xác minh.
- Độ tin cậy thấp — thử thách tương tác đầy đủ, hoặc bị chặn thẳng.
Với tự động hóa, managed phổ biến nhất nhưng cũng biến thiên nhiều nhất — khó đoán trước chỉ bằng cách đọc HTML tĩnh.
Phát hiện managed mode trong HTML
def is_managed_mode(html):
"""Check if Turnstile is using managed mode (default)."""
# Managed mode is the default — no explicit mode attribute
has_turnstile = "cf-turnstile" in html
has_explicit_mode = 'data-appearance="interaction-only"' in html or \
'data-appearance="always"' in html or \
'appearance: "interaction-only"' in html
return has_turnstile and not has_explicit_mode
Non-interactive mode: không checkbox, chỉ có spinner
Non-interactive không bao giờ hiện checkbox hay phần tử tương tác nào.
Nó chạy proof-of-work ở nền, chỉ hiện một spinner đang tải. Nếu thử thách không hoàn thành được mà không cần tương tác, nó báo lỗi luôn thay vì leo thang lên checkbox.
Cách triển khai
<!-- Non-interactive mode -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-appearance="interaction-only">
</div>
Hoặc qua JavaScript API:
turnstile.render('#turnstile-container', {
sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
appearance: 'interaction-only',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
},
});
Luồng xử lý
Page loads → Widget initializes
↓
Background proof-of-work runs
↓
Success → Token generated (no visible UI)
OR
Failure → Widget reports error (no fallback to checkbox)
Site nào hay chọn non-interactive
Vài trường hợp thường gặp:
- Form comment và widget phản hồi
- Đăng ký nhận bản tin (newsletter)
- Hành động giá trị thấp, cần ma sát tối thiểu
- Endpoint API có lớp bảo vệ phía trình duyệt
Invisible mode: không widget nào xuất hiện
Invisible đúng nghĩa vô hình — không có phần tử container nào xuất hiện trong viewport.
Widget chạy ngay khi trang tải, hoặc khi bị trigger bằng code, rồi tạo token mà không để lại dấu hiệu trực quan nào.
Cách triển khai
<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
data-size="invisible">
</div>
Hoặc hoàn toàn bằng JavaScript:
// Programmatic invisible Turnstile
turnstile.render('#hidden-container', {
sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
size: 'invisible',
callback: function(token) {
// Token ready — submit form automatically
submitForm(token);
},
'error-callback': function() {
// Challenge failed
console.error('Invisible Turnstile failed');
},
});
Vì sao invisible khó phát hiện hơn
Container không có kích thước hiển thị nên bạn không thể chỉ nhìn DOM để kết luận:
import re
def detect_invisible_turnstile(html):
"""Detect invisible Turnstile on a page."""
indicators = {
"script_loaded": "challenges.cloudflare.com/turnstile" in html,
"size_invisible": 'data-size="invisible"' in html or
"size: 'invisible'" in html or
'size: "invisible"' in html,
"api_render_call": "turnstile.render" in html,
"response_field": "cf-turnstile-response" in html,
}
if indicators["script_loaded"] and indicators["size_invisible"]:
return {"mode": "invisible", "confidence": "high"}
elif indicators["script_loaded"] and indicators["api_render_call"]:
return {"mode": "invisible_or_programmatic", "confidence": "medium"}
elif indicators["response_field"]:
return {"mode": "turnstile_present", "confidence": "low"}
return {"mode": "none", "confidence": "high"}
Trích sitekey bất kể site dùng chế độ nào
Dù là managed, non-interactive hay invisible, sitekey luôn là tham số bắt buộc để giải.
Lấy nó từ bất kỳ dạng render nào:
import re
def extract_turnstile_sitekey(html):
"""Extract Turnstile sitekey from page HTML (works for all modes)."""
# Pattern 1: data-sitekey attribute in HTML
match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html)
if match:
return match.group(1)
# Pattern 2: JavaScript render call
match = re.search(r"sitekey:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
if match:
return match.group(1)
# Pattern 3: Turnstile config object
match = re.search(r"siteKey['\"]?\s*[:=]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
if match:
return match.group(1)
return None
Giải cả 3 chế độ Turnstile bằng một API CaptchaAI
Cả ba chế độ Turnstile được CaptchaAI giải giống hệt nhau.
Chế độ không ảnh hưởng đến lệnh gọi API — bạn không cần khai báo trước site đang chạy managed, non-interactive hay invisible.
Tình huống quen thuộc với nhóm QA outsourcing ở TP.HCM: client đổi cấu hình Cloudflare khác nhau giữa dev, staging, production — staging chạy invisible, production chạy managed. Script chỉ cần đúng sitekey và pageurl, không cần code riêng cho từng môi trường.
Ba thứ cần chuẩn bị: sitekey, pageurl, và API key CaptchaAI.
Python
Code chạy được ngay, chỉ cần đổi API_KEY:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_turnstile(sitekey, page_url):
"""Solve any Turnstile mode — managed, non-interactive, or invisible."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": page_url,
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Turnstile solve timed out")
# Use with any mode
token = solve_turnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
print(f"Token: {token[:50]}...")
Node.js
Cùng logic, viết lại bằng axios:
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveTurnstile(sitekey, pageUrl) {
const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "turnstile",
sitekey,
pageurl: pageUrl,
json: 1,
},
});
const taskId = submit.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId, json: 1 },
});
if (result.data.status === 1) {
return result.data.request;
}
}
throw new Error("Turnstile solve timed out");
}
// Same function works for all Turnstile modes
solveTurnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
.then((token) => console.log("Token:", token.substring(0, 50)));
Câu hỏi thường gặp về chế độ Turnstile
Turnstile invisible có tốn nhiều thread hơn managed không?
Không. Thread trong bảng giá CaptchaAI đếm theo số task đang giải đồng thời, không phân biệt theo chế độ Turnstile.
Gói BASIC ($15/tháng, 5 thread) cho phép giải tối đa 5 task Turnstile cùng lúc, dù đó là managed, non-interactive hay invisible.
CaptchaAI có giải được cả ba chế độ Turnstile chỉ bằng một API không?
Có. CaptchaAI dùng đúng một method turnstile cho cả ba chế độ.
Sitekey và pageurl là hai tham số bắt buộc duy nhất — bạn không cần khai báo chế độ, định dạng token và luồng xác thực không đổi.
Sitekey có đổi khi site chuyển giữa các chế độ không?
Hiếm khi. Site có thể chuyển từ managed sang non-interactive cho một số trang hoặc phân đoạn người dùng, nhưng sitekey thường giữ nguyên.
Chỉ cách render đổi — luôn phát hiện lại chế độ mỗi lần điều hướng.
Vì sao lấy được token invisible nhưng form vẫn báo lỗi?
Nguyên nhân phổ biến nhất là script trích nhầm sitekey — widget ẩn đôi khi dùng sitekey khác với sitekey xuất hiện trong DevTools lúc debug.
Nguyên nhân thứ hai là token đã hết hạn 300 giây trước khi submit. Dùng hàm trích sitekey ở trên ngay trước khi giải, đừng cache sitekey từ lần crawl trước.
Non-interactive phù hợp với form nào trong sản phẩm thực tế?
Non-interactive hợp với các hành động giá trị thấp mà ma sát phải tối thiểu: form comment, đăng ký nhận bản tin, hoặc endpoint API chỉ cần lớp bảo vệ phía trình duyệt.
Với luồng đăng nhập hoặc đăng ký — nơi Cloudflare cần đánh giá rủi ro sâu hơn — managed vẫn là lựa chọn phổ biến hơn.
Các lỗi thường gặp khi tích hợp Turnstile
Bốn nhóm lỗi chiếm phần lớn ticket hỗ trợ Turnstile:
| Hiện tượng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Token hợp lệ nhưng form vẫn từ chối | Lấy nhầm sitekey (khác với sitekey đang render trên widget) | Kiểm tra sitekey được JavaScript render ra, không chỉ đọc HTML tĩnh |
| Không tìm thấy widget trong HTML | Invisible mode được load sau lần render đầu tiên | Đợi trang tải xong hoàn toàn, kiểm tra các response XHR |
| Nhiều widget Turnstile trên cùng một trang | Mỗi form dùng sitekey riêng | Khớp đúng sitekey với từng form cụ thể |
data-size="compact" gây nhầm khi phát hiện |
Compact là biến thể kích thước, không phải một chế độ | Compact mặc định vẫn chạy managed mode |
Có thuộc tính data-action |
Tag phục vụ phân tích (analytics), không phải chế độ | Đưa action vào khi giải nếu site yêu cầu để xác thực |
| Token hết hạn trước khi submit | Token Turnstile hết hạn sau 300 giây | Giải ngay trước khi submit form, không cache lâu |
Tóm tắt
Ba điều cần nhớ về các chế độ widget Cloudflare Turnstile:
- Managed, non-interactive, invisible kiểm soát trải nghiệm người dùng, nhưng cả ba luôn trả về cùng một token
cf-turnstile-response. - Với tự động hóa, cả ba chế độ được bộ giải Turnstile của CaptchaAI xử lý theo đúng một method
turnstile— sitekey và pageurl là đủ, tỷ lệ giải thành công cao trên mọi biến thể. - Khác biệt thật sự nằm ở khâu phát hiện: managed để lại dấu vết rõ trong HTML tĩnh, còn invisible đòi hỏi phân tích sâu hơn — thường phải đợi JavaScript render xong — mới tìm ra sitekey.