لكل إخفاق في Cloudflare Turnstile سبب واحد قابل للتحديد، وأكثر الحالات إحباطًا أن تُعيد واجهة CaptchaAI رمزًا سليمًا ثم ترفضه الصفحة رغم ذلك. مفتاح الإصلاح السريع أن تعرف المرحلة التي انكسر عندها المسار قبل أن تجرّب أي حل عشوائي؛ فكل فشل في Turnstile يقع في واحدة من ثلاث مراحل واضحة:
| المرحلة | ما الذي انكسر | العَرَض النموذجي |
|---|---|---|
| الطلب | رُفض إرسالك إلى الـ API قبل بدء الحل | رمز خطأ فوري لحظة الإرسال |
| النتيجة | قُبل الطلب لكنّ الاستطلاع تعثّر أو انتهت مهلته | CAPCHA_NOT_READY لا ينتهي أو مهلة زمنية |
| التحقق في الصفحة | عاد رمز صالح لكنّ الصفحة رفضته | النموذج يُعاد تحميله أو يظهر خطأ تحقّق |
بعد تحديد المرحلة يصبح الإصلاح مباشرًا. والأسباب الثلاثة الأكثر تكرارًا خلف هذه الإخفاقات هي: عنوان صفحة (pageurl) غير دقيق — خصوصًا على صفحات تحدّي Cloudflare حيث يكون السياق أشد صرامة؛ ومفتاح موقع (sitekey) مأخوذ من عنصر خاطئ أو من نسخة مختلفة من الأداة؛ ورمز طُبِّق عبر المسار الخطأ لأن الصفحة تنتظره في cf-turnstile-response أو في دالة رد نداء أو في كليهما.
يحلّ CaptchaAI اختبار Turnstile بمعدل نجاح مرتفع في أقل من 10 ثوانٍ، لذلك حين يفشل التكامل تكون العلة غالبًا في المعلمات التي ترسلها أو في كيفية تطبيق الرمز المُعاد، لا في الخدمة نفسها. في ما يلي نمرّ على المراحل الثلاث بالترتيب، مع رموز الخطأ وحلولها وأمثلة عمل جاهزة.
لماذا يختلف Turnstile عن بقية أنواع CAPTCHA
قبل الدخول في رموز الخطأ، ثمّة خصائص تُميّز Turnstile عن غيره وتفسّر معظم حالات الرفض:
- عنوان الصفحة (pageurl) يجب أن يكون دقيقًا — ترتبط رموز Turnstile بسياق الصفحة ارتباطًا وثيقًا. وعلى صفحات تحدّي Cloudflare — شاشة التحقق بملء الصفحة — يكفي اختلاف بسيط في المسار أو معلمة استعلام مفقودة كي يُرفض الرمز.
- الرمز صالح لمرة واحدة فقط — بمجرد أن يتحقق خادم Cloudflare من الرمز يُبطله. فإن أرسلته أتمتتك مرتين بالخطأ، أو حدث تعارض توقيت (race condition)، فشلت المحاولة الثانية حتمًا، ولا تنفع أي طبقة تخزين مؤقت (cache) هنا.
أما الخاصية الثالثة فهي أنّ للرمز مسارَي تسليم، واختيار الطريق الخطأ يفشل بصمت دون رسالة واضحة:
| الطريقة | متى تستخدمها |
|---|---|
حقل مخفي — أدخل الرمز في cf-turnstile-response (وأحيانًا g-recaptcha-response) |
عندما تعتمد الصفحة على نموذج قياسي بحقول مخفية |
دالة رد النداء — استدعِ الدالة المُعرّفة في turnstile.render() أو في data-callback |
عندما تعتمد الصفحة على تحقّق برمجي بدل النموذج |
المرحلة الأولى: أخطاء إرسال الطلب
تظهر هذه الأخطاء لحظة إرسال المهمة إلى https://ocr.captchaai.com/in.php، ومعظمها متعلق ببيانات الاعتماد أو الرصيد ويُحلّ في ثوانٍ. يلخّص الجدول التالي أكثرها شيوعًا:
| الخطأ | السبب | الإصلاح |
|---|---|---|
ERROR_WRONG_USER_KEY |
صيغة مفتاح الـ API غير صحيحة (يجب أن يكون 32 حرفًا) | راجع المفتاح من captchaai.com/api.php |
ERROR_KEY_DOES_NOT_EXIST |
المفتاح صحيح الصياغة لكنه غير مرتبط بحساب نشط | افتح لوحة التحكم وتأكّد أنّ الحساب مفعّل والمفتاح صحيح |
ERROR_ZERO_BALANCE |
لا توجد خيوط معالجة (Threads) متاحة في خطتك حاليًا | انتظر تحرّر أحد الخيوط، أو قلّل التزامن، أو رقِّ الخطة |
| استجابات HTML أو 500/502 | خطأ عابر من جهة الخادم | انتظر 5–10 ثوانٍ ثم أعد المحاولة |
الخطآن التاليان يحتاجان تفصيلًا أوسع لأنهما الأكثر إرباكًا في الإعداد الأول.
ERROR_PAGEURL
السبب: المعلمة pageurl مفقودة تمامًا من الطلب.
الإصلاح: أضف العنوان الكامل شاملًا البروتوكول والنطاق والمسار، لا النطاق وحده:
pageurl=https://example.com/login
ERROR_BAD_PARAMETERS
السبب: معلمة مطلوبة مفقودة أو مشوّهة. المعلمات المطلوبة لـ Turnstile هي:
| المعلمة | النوع | مطلوبة | الوصف |
|---|---|---|---|
key |
نص | نعم | مفتاح CaptchaAI API الخاص بك |
method |
نص | نعم | يجب أن تساوي turnstile |
sitekey |
نص | نعم | مفتاح موقع عنصر Turnstile |
pageurl |
نص | نعم | عنوان الصفحة الكامل |
ومعلمات اختيارية لكنها مفيدة، خصوصًا على الصفحات المحمية بقوة:
| المعلمة | النوع | الوصف |
|---|---|---|
action |
نص | قيمة data-action أو معلمة action من turnstile.render() |
proxy |
نص | التنسيق: login:password@IP:PORT |
proxytype |
نص | HTTP أو HTTPS أو SOCKS4 أو SOCKS5 |
الإصلاح: تأكّد من وجود كل الحقول المطلوبة وصحّة نوعها وترميزها قبل الإرسال.
كيف تعثر على مفتاح الموقع (sitekey) الصحيح
مفتاح الموقع هو المعلمة الأكثر عرضة للخطأ، والتقاطه من العنصر الصحيح يحسم معظم حالات الفشل مبكرًا. إليك ثلاث طرق لاستخراجه:
الطريقة الأولى — السمة data-sitekey:
<div class="cf-turnstile" data-sitekey="0x4AAAAAAAB1example"></div>
الطريقة الثانية — استدعاء turnstile.render():
turnstile.render('#captcha-container', {
sitekey: '0x4AAAAAAAB1example',
callback: function(token) {
document.getElementById('cf-turnstile-response').value = token;
}
});
الطريقة الثالثة — اعتراض استدعاء العرض (متقدّم):
إن كان مفتاح الموقع يُحمَّل ديناميكيًا، فأعد تعريف turnstile.render قبل تهيئة الأداة كي تلتقط معلماتها لحظة تمريرها:
// Inject this before the Turnstile script loads
const originalRender = window.turnstile.render;
window.turnstile.render = function(container, params) {
console.log('Sitekey:', params.sitekey);
console.log('Action:', params.action);
return originalRender.call(this, container, params);
};
المرحلة الثانية: أخطاء انتظار النتيجة
تظهر هذه الأخطاء أثناء استطلاع النتيجة من https://ocr.captchaai.com/res.php. أغلبها مؤقّت ويُعالَج بإعادة المحاولة بعد ثوانٍ قليلة:
| الخطأ | المعنى | الإصلاح |
|---|---|---|
CAPCHA_NOT_READY |
ليس خطأً؛ الحل ما زال جاريًا (عادةً أقل من 10 ثوانٍ) | انتظر 5 ثوانٍ ثم أعد الاستطلاع |
ERROR_WRONG_ID_FORMAT |
معرّف الـ CAPTCHA يحتوي على أحرف غير رقمية | استخدم المعرّف كما أعاده in.php تمامًا |
ERROR_WRONG_CAPTCHA_ID |
المعرّف لا يطابق أي مهمة مُرسَلة | تحقّق من معرّف الإرسال الصحيح |
ERROR_CAPTCHA_UNSOLVABLE |
فشل الحل — غالبًا مفتاح موقع خاطئ أو إعداد صفحة غير مدعوم | راجع مفتاح الموقع، جدّد الطلب، ثم أعد المحاولة |
ERROR_INTERNAL_SERVER_ERROR |
مشكلة من جهة الخادم | انتظر 10 ثوانٍ ثم أعد المحاولة |
وإن تكرّر ERROR_CAPTCHA_UNSOLVABLE مع مفتاح موقع مؤكَّد الصحّة، فغالبًا أنّ إعداد الصفحة غير مدعوم؛ تحقّق حينها من أنك ترسل قيمة action الصحيحة إن كانت الصفحة تشترطها. أما الخطأ الأكثر شيوعًا في هذه المرحلة فيستحق تفصيلًا مستقلًا.
ERROR_EMPTY_ACTION
السبب: المعلمة action مفقودة من طلب الاستطلاع.
الإصلاح: ضمِّن دائمًا action=get:
https://ocr.captchaai.com/res.php?key=YOUR_KEY&action=get&id=CAPTCHA_ID&json=1
ملاحظة: استخدم دائمًا
json=1في طلب استطلاع Turnstile. فقد تتضمّن استجابة JSON قيمةuser_agentالخاصة بالحل، وتشترطها بعض الصفحات المحمية بـ Cloudflare للتحقق من الرمز بنجاح.
المرحلة الثالثة: الصفحة ترفض رمزًا صالحًا
هذه أصعب الحالات في التشخيص، لأن الـ API يعيد رمزًا سليمًا بينما ترفضه الصفحة المستهدفة. إليك أشيع أربعة أسباب وحلولها.
السبب 1: الرمز أُدخل في الحقل الخطأ
العَرَض: يُرسَل النموذج لكن الصفحة تُظهر خطأ تحقّق أو تُعيد التحميل.
قد تنتظر صفحات Turnstile الرمز في حقول مختلفة:
cf-turnstile-response— الحقل المخفي الأساسي في Turnstileg-recaptcha-response— تستخدمه بعض الصفحات كخيار احتياطي
الإصلاح: افحص نموذج الصفحة بحثًا عن الحقلين، وأدخل الرمز فيهما معًا في أتمتة المتصفح:
# Selenium — inject into both fields for safety
driver.execute_script("""
var cfField = document.querySelector('[name="cf-turnstile-response"]');
var gField = document.querySelector('[name="g-recaptcha-response"]');
if (cfField) cfField.value = arguments[0];
if (gField) gField.value = arguments[0];
""", token)
السبب 2: دالة رد النداء لم تُستدعَ
العَرَض: الرمز موجود في الحقل، لكن النموذج ما زال يمنع الإرسال.
السبب: تستخدم الصفحة دالة رد نداء بدلًا من الحقل المخفي أو إضافةً إليه. وتتولى هذه الدالة منطقًا إضافيًا مثل تفعيل زر الإرسال أو إطلاق طلب AJAX.
الإصلاح: ابحث عن دالة رد النداء واستدعِها بنفسك:
// Check data-callback attribute
const callbackName = document.querySelector('.cf-turnstile').getAttribute('data-callback');
if (callbackName && window[callbackName]) {
window[callbackName](token);
}
// Or if it was passed in turnstile.render()
// You may need to intercept the render call to capture it
السبب 3: سياق الصفحة الدقيق غير مطابق
العَرَض: رُفض الرمز رغم صحة مفتاح الموقع وحداثة الحل.
السبب: عنوان pageurl المُرسَل في طلب الـ API لا يطابق السياق الفعلي للصفحة. وهذا شائع بوجه خاص في:
- صفحات تحدّي Cloudflare — قد يحمل العنوان معلمات استعلام أو أجزاء مسار مؤثّرة.
- تطبيقات الصفحة الواحدة (SPA) — قد يختلف العنوان الظاهر عن العنوان الذي حمّل أداة Turnstile.
الإصلاح: افتح تبويب Network في DevTools وحدّد العنوان الدقيق الذي تُحمَّل منه الأداة، واستخدمه قيمةً لـ pageurl.
السبب 4: إعادة استخدام الرمز
العَرَض: ينجح الحل الأول ثم تفشل الحلول التالية.
السبب: رموز Turnstile صالحة لمرة واحدة؛ فبمجرد أن يتحقق منها خادم Cloudflare تُبطَل نهائيًا.
الإصلاح: اطلب حلًا جديدًا لكل إرسال نموذج، ولا تخزّن الرموز أو تعيد استخدامها في طلبٍ لاحق.
سيناريو من الميدان
تخيّل فريقًا في القاهرة يبني أداة مراقبة أسعار لبوابة حجوزات إقليمية محمية بعنصر Turnstile مضمَّن. كان الحل ينجح على صفحة تسجيل الدخول ويفشل باستمرار على صفحة نتائج البحث، مع أن مفتاح الموقع والرمز صحيحان في الحالتين. تبيّن أن السبب من النوع الثالث تمامًا: البوابة تطبيق صفحة واحدة (SPA)، والعنوان الظاهر في المتصفح كان /search/results بينما حُمِّلت الأداة عند /search — ففشل التحقق لأن pageurl المُرسَل لم يطابق العنوان الذي وُلِّد عنده الرمز. ومع أن الرمز كان جديدًا وصحيحًا في كل مرة، فإن اختلاف العنوان وحده كان كافيًا لرفضه من طرف Cloudflare.
القاعدة العملية التي يستخلصها الفريق: على أي تطبيق SPA أو صفحة تحدٍّ، لا تفترض العنوان من شريط المتصفح، بل اقرأه من الطلب الذي حمّل أداة Turnstile فعليًا. وإن كنت تصطدم برمز 403 بعد تصحيح الرمز، فراجع دليل Cloudflare Turnstile 403 بعد إصلاح الرمز لأنّه يعالج حالة قريبة لكنها مختلفة السبب.
Turnstile مقابل Cloudflare Challenge: أيّهما تواجه؟
| المؤشّر | Turnstile | Cloudflare Challenge |
|---|---|---|
| ما تراه | عنصر مضمَّن داخل الصفحة — مربع اختيار أو غير مرئي | شاشة تحقّق بملء الصفحة من Cloudflare |
| ما يعيده CaptchaAI | رمز يُحقن في النموذج | ملف تعريف ارتباط cf_clearance |
| طريقة الـ API | turnstile |
cloudflare_challenge |
| هل الـ proxy مطلوب؟ | اختياري | نعم — إلزامي |
إن كنت أمام تحدّي Cloudflare بملء الصفحة — لا عنصرًا مضمَّنًا — فأنت بحاجة إلى حل Cloudflare Challenge بدلًا من ذلك، وهو يعيد ملف تعريف الارتباط cf_clearance ويتطلب proxy.
الحل الكامل بلغة Python
يجمع المثال التالي مرحلتَي الإرسال والاستطلاع في دالة واحدة: يرسل المهمة، ينتظر عشر ثوانٍ (فالحل سريع عادةً)، ثم يستطلع النتيجة كل خمس ثوانٍ حتى تجهز أو تنتهي المهلة، ويعيد الرمز جاهزًا للحقن في النموذج.
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
SITEKEY = "0x4AAAAAAAB1example"
PAGE_URL = "https://example.com/login"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_turnstile(api_key, sitekey, pageurl):
"""Submit a Turnstile challenge and return the solved token."""
# Submit
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": pageurl,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Wait before first poll (Turnstile is fast — 10 seconds is usually enough)
time.sleep(10)
# Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return result_data["request"]
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("Turnstile solve timed out")
# Usage
token = solve_turnstile(API_KEY, SITEKEY, PAGE_URL)
print(f"Solved token: {token[:80]}...")
# Inject into cf-turnstile-response and/or g-recaptcha-response
# Then submit the form
الحل الكامل بلغة Node.js
المنطق نفسه بلغة Node.js باستخدام fetch دون أي تبعيات خارجية:
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const SITEKEY = "0x4AAAAAAAB1example";
const PAGE_URL = "https://example.com/login";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveTurnstile(apiKey, sitekey, pageurl) {
// Submit
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "turnstile",
sitekey: sitekey,
pageurl: pageurl,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
// Turnstile is fast — wait 10 seconds before first poll
await sleep(10_000);
// Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return resultData.request;
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("Turnstile solve timed out");
}
// Usage
solveTurnstile(API_KEY, SITEKEY, PAGE_URL)
.then((token) => {
console.log(`Solved token: ${token.slice(0, 80)}...`);
// Inject into cf-turnstile-response and/or g-recaptcha-response
})
.catch(console.error);
الأسئلة الشائعة
هل يعني ERROR_ZERO_BALANCE أنّ رصيدي المالي قد نفد؟
لا بالضرورة. تعتمد خطط CaptchaAI على عدد خيوط المعالجة المتزامنة لا على عدد الحلول، وكل خيط يعالج طلبًا واحدًا في اللحظة الواحدة ثم يتحرّر للطلب التالي. يظهر هذا الخطأ حين تكون كل خيوطك مشغولة في الوقت نفسه؛ فانتظر تحرّر أحدها، أو خفّض مستوى التزامن، أو انتقل إلى خطة ذات خيوط أكثر.
ما الطول الصحيح لمفتاح الـ API، وكيف أتأكّد منه؟
مفتاح CaptchaAI مكوّن من 32 حرفًا. إن ظهر ERROR_WRONG_USER_KEY فالمفتاح غالبًا مقطوع أو يحوي فراغات؛ انسخه كاملًا من لوحة التحكم وتأكّد من خلوّه من أي مسافات زائدة قبل إرساله.
لماذا ينجح أول حل ثم تفشل الطلبات التالية؟
لأن رموز Turnstile صالحة لمرة واحدة. فبمجرد أن يتحقق خادم Cloudflare من الرمز يُبطله، ولا يمكن إعادة استخدامه في إرسال ثانٍ. اطلب حلًا جديدًا مستقلًا لكل عملية إرسال، ولا تخزّن الرموز لاستخدامها لاحقًا.
متى أحتاج إلى proxy مع Turnstile؟
الـ proxy اختياري لعناصر Turnstile المضمَّنة المستقلة، لكنه موصى به بشدّة — وقد يكون إلزاميًا — على صفحات تحدّي Cloudflare بملء الصفحة. لإضافته مرّر معلمتَي proxy وproxytype ضمن طلبك.
كيف أحدّد pageurl الصحيح في تطبيقات الصفحة الواحدة (SPA)؟
لا تعتمد على العنوان الظاهر في شريط المتصفح، فقد يختلف عن العنوان الذي حمّل أداة Turnstile. افتح تبويب Network في DevTools، وابحث عن الطلب الذي حمّل الأداة، واستخدم عنوانه الكامل — بما فيه المسار ومعلمات الاستعلام — قيمةً لـ pageurl.
هل يكفي json=1 وحده لعلاج رفض الصفحة للرمز؟
ليس دائمًا، لكنه خطوة مهمة. استجابة json=1 قد تحمل قيمة user_agent التي تربطها بعض صفحات Cloudflare بالرمز عند التحقق؛ استخدام هذه القيمة نفسها في متصفحك يرفع فرص القبول. أما إن استمر الرفض فراجع اسم الحقل ودالة رد النداء ودقّة pageurl بالترتيب.
أصلح تكامل Turnstile في خمس خطوات
إن استمر تكامل Turnstile في الفشل، راجع هذه النقاط بالترتيب:
- تحقّق من مفتاح الموقع — استخرجه من
data-sitekeyأو منturnstile.render(). - تحقّق من عنوان الصفحة — استخدم العنوان الدقيق شاملًا البروتوكول والمسار.
- تحقّق من مسار الرمز — هل تنتظره الصفحة في
cf-turnstile-responseأمg-recaptcha-responseأم في رد نداء؟ - استخدم
json=1— اعتمد استجابات JSON دائمًا عند استطلاع نتائج Turnstile. - لا تُعد استخدام الرموز — اطلب حلًا جديدًا لكل إرسال.
ابدأ من حل Turnstile عبر CaptchaAI، وطابق معلماتك مع مستندات الـ API، واقرأ كيف يعمل Cloudflare Turnstile إن أردت خلفية عن آلية عمل الأداة.