إذا كان الخادم يرفض رمز reCAPTCHA رغم نجاح الحل، فالسبب غالباً ليس الرمز نفسه بل النطاق. يربط reCAPTCHA كل رمز باسم المضيف الذي تولّد عليه، فإذا أُرسل إلى نطاق يختلف عن قيمة pageurl التي مرّرتها للحل، يرفضه الخادم بصمت — تبدو الاستجابة سليمة لكن التحقق يفشل. نبدأ من أكثر الأسباب تكراراً — عدم تطابق pageurl — ثم نمرّ على كل خطأ نطاق وطريقة إصلاحه في الأتمتة.
كيف يربط reCAPTCHA الرمز بالنطاق
يمرّ التحقق من النطاق بسلسلة ثابتة، وأي حلقة تنكسر فيها تنتج خطأ نطاق:
- يسجّل مالك الموقع النطاقات المسموح بها لمفتاح الموقع.
- يولّد المتصفح رمزاً يحمل اسم المضيف بداخله عند ظهور الأداة.
- يتحقق الخادم من الرمز عبر siteverify فيقارن اسم المضيف بالنطاقات المسجّلة.
Site owner registers reCAPTCHA → adds allowed domains (example.com, www.example.com)
↓
reCAPTCHA widget loads on example.com → matches allowed domain ✓
↓
Token generated with embedded hostname
↓
Server validates token via siteverify API
↓
Google checks: Does token hostname match allowed domains?
├─ YES → { "success": true, "hostname": "example.com" }
└─ NO → { "success": false, error or hostname mismatch }
أين يُفحص النطاق بالضبط
يجري الفحص عند ثلاث نقاط، لكل منها دور:
| نقطة الفحص | ما الذي يُتحقق منه |
|---|---|
| جانب العميل (المتصفح) | لا تُحمّل الأداة إلا على النطاقات المسموح بها — فحص اختياري يمكن لمالك الموقع تعطيله |
| توليد الرمز | اسم المضيف المضمَّن في الرمز يطابق أصل الصفحة التي ظهرت فيها الأداة |
| تحقّق الخادم | تُعيد siteverify اسم المضيف، وعلى خادم الموقع أن يقرر قبوله أو رفضه |
أبرز أخطاء التحقق من النطاق ومعناها
الخطأ الأول: عدم تطابق اسم المضيف في استجابة siteverify
{
"success": true,
"hostname": "subdomain.example.com",
"challenge_ts": "2025-01-15T10:30:00Z"
}
الرمز صالح فعلاً (success: true)، لكن حقل hostname يشير إلى نطاق غير متوقَّع، وبعض تطبيقات التحقق ترفض هذه الحالة صراحةً:
# Server-side validation that checks hostname
def validate_token(token, secret_key, expected_hostname):
result = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": secret_key, "response": token},
).json()
if not result.get("success"):
return False
# This check causes failures when hostnames don't match
if result.get("hostname") != expected_hostname:
return False # Domain mismatch!
return True
متى يحدث ذلك؟
- حُلّ الرمز على
www.example.comبينما يجري التحقق منه علىexample.com - حُلّ الرمز على
staging.example.comثم أُرسل للتحقق على نطاق الإنتاجexample.com - يغيّر خادم وسيط أو شبكة CDN اسم المضيف الظاهر
الإصلاح
اجعل قيمة pageurl في طلب الحل مطابقة تماماً للنطاق الذي سيُرسَل إليه الرمز لاحقاً.
الخطأ الثاني: الأداة ترفض الظهور من الأساس
لا تظهر أداة reCAPTCHA وتطبع رسالة خطأ بدلاً من ذلك:
ERROR: Invalid domain for site key
متى يحدث ذلك؟
- النطاقات المسموح بها لمفتاح الموقع لا تتضمن نطاق الصفحة الحالية
- تحميل الأداة من
localhostأو عبر بروتوكولfile:// - استخدام عنوان IP بدل اسم النطاق
الإصلاح في الأتمتة
هذا خطأ في إعدادات مالك الموقع لا في كودك أنت. من جانب الحل، يكفي أن تمرّر pageurl صحيحاً يقع ضمن النطاقات المسموح بها.
الخطأ الثالث: رمز صحيح لكنه مرفوض
{
"success": false,
"error-codes": ["invalid-input-response"]
}
هنا success: false مع invalid-input-response، أي أن الرمز تولّد لنطاق يختلف عن نطاق التحقق.
السبب الأكثر شيوعاً في الأتمتة
قيمة pageurl المُرسَلة إلى الخدمة لا تطابق النطاق المستهدف الفعلي:
# WRONG: pageurl doesn't match actual target
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": "https://example.com/login", # ← Must match actual domain
"json": 1,
})
# But submitting token to:
requests.post("https://app.example.com/login", ...) # Different subdomain!
جدول سريع لتشخيص الأعطال
قبل الإصلاحات التفصيلية، ابدأ من العرَض ثم اتبع التشخيص المقابل له:
| العرَض | السبب المرجّح | التشخيص | الإصلاح |
|---|---|---|---|
| الرمز مرفوض دائماً | pageurl لا يطابق النطاق المستهدف | قارن pageurl المُرسَل للحل بنطاق الإرسال الفعلي | حدّث pageurl ليطابق النطاق |
| ينجح على www ويفشل على غير www (أو العكس) | اختلاف صيغة النطاق | افحص سلوك إعادة التوجيه | استخدم الصيغة التي يعتمدها الموقع فعلاً (بتتبّع إعادة التوجيه) |
| ينجح أحياناً ويفشل أحياناً | شبكة CDN أو موازن تحميل يقدّم نطاقات مختلفة | تحقق إن كان النطاق يتغيّر بين طلب وآخر | ثبّت نطاقاً واحداً من سلسلة إعادة التوجيه |
| ينجح في المتصفح ويفشل في السكربت | السكربت يرسل من أصل مختلف | قارن شريط عنوان المتصفح بقيمة pageurl في السكربت | طابق النطاق النهائي الذي يظهر في المتصفح |
| رمز Enterprise مرفوض | مشروع خاطئ أو ربط نطاق غير صحيح | تأكد أن مفتاح موقع Enterprise مربوط بالنطاق | راجع إعدادات النطاق في لوحة Enterprise |
قواعد مطابقة النطاق: متى يُقبل النطاق الفرعي؟
تحقّق reCAPTCHA من النطاق ليس مطابقة صارمة افتراضياً؛ فالسلوك يعتمد على إعدادات مالك الموقع، والجدول التالي يلخّص الأصول المقبولة حسب النطاق المسجّل:
| النطاق المسجّل | الأصول المقبولة |
|---|---|
example.com |
example.com وwww.example.com وsub.example.com (إذا فُعّل حرف البدل) |
www.example.com |
www.example.com فقط (في الوضع الصارم) |
*.example.com |
أي نطاق فرعي من example.com |
localhost |
localhost فقط (للتطوير المحلي) |
كيف يتصرف الخادم مع اسم المضيف
يعكس حقل hostname الصفحة التي تولّد عليها الرمز، ويبقى القرار للخادم: قبول متساهل يسمح بأي نطاق فرعي، أو صارم يشترط تطابقاً حرفياً.
# Permissive validation (accepts any subdomain)
def validate_permissive(token, secret, base_domain):
result = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": secret, "response": token},
).json()
if not result.get("success"):
return False
hostname = result.get("hostname", "")
return hostname == base_domain or hostname.endswith(f".{base_domain}")
# Strict validation (exact match only)
def validate_strict(token, secret, expected_hostname):
result = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": secret, "response": token},
).json()
return result.get("success") and result.get("hostname") == expected_hostname
مثال عملي: نطاق يعيد التوجيه في متجر عربي
تخيّل متجراً سعودياً نطاقه الجذر matjar.sa، لكنه يعيد الزوار إلى www.matjar.sa، وصفحة الدخول تنتقل إلى نطاق فرعي للمصادقة مثل account.matjar.sa. لو مرّرت pageurl بقيمة https://matjar.sa/login بينما يُرسَل الرمز فعلياً من account.matjar.sa، فسيحمل الرمز اسم مضيف خاطئاً ويُرفض بصمت.
الحل خطوتان:
- اتبع سلسلة إعادة التوجيه (كما في الإصلاح الثالث أدناه) للوصول إلى النطاق النهائي.
- استخدم ذلك النطاق بالضبط في
pageurlعند إرسال طلب الحل.
وهذا النمط شائع في منصّات المنطقة التي تفصل نطاق المصادقة عن الرئيسي.
إصلاح أخطاء النطاق في الأتمتة
معظم أخطاء النطاق تُحل بضبط pageurl بدقة، وفيما يلي أربع طرق من الأبسط إلى الأعمق:
الإصلاح الأول: اجعل pageurl مطابقاً تماماً للهدف
أكثر الإصلاحات شيوعاً هو أن يطابق pageurl النطاق الذي سيُرسَل إليه الرمز:
# Correct: pageurl matches where you'll submit the token
target_url = "https://www.example.com/login"
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
"pageurl": target_url, # Must match the actual domain
"json": 1,
})
الإصلاح الثاني: التعامل مع www وغير www
إن لم تعرف الصيغة التي يعتمدها الموقع، اطلب الصفحة واترك المكتبة تتبع إعادة التوجيه:
from urllib.parse import urlparse
def normalize_url(url):
"""Normalize URL for consistent domain matching."""
parsed = urlparse(url)
# Use exactly what the target site uses
# Check if the site redirects www → non-www or vice versa
return f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
# Test which variant the site uses
response = requests.get("https://example.com/login", allow_redirects=True)
actual_url = response.url # May be https://www.example.com/login after redirect
الإصلاح الثالث: اكتشاف النطاق الصحيح من سلسلة إعادة التوجيه
بعض المواقع تعيد التوجيه عبر أكثر من نطاق قبل الصفحة النهائية:
def get_final_url(url):
"""Follow redirects to find the actual CAPTCHA page domain."""
response = requests.get(url, allow_redirects=True, timeout=15)
return response.url
# Login URL might redirect:
# https://example.com/login → https://auth.example.com/login
final_url = get_final_url("https://example.com/login")
# Use final_url as pageurl for solver
الإصلاح الرابع: استخراج النطاق من إطار reCAPTCHA
حين يصعب تحديد النطاق من العنوان وحده، اقرأ إطار (iframe) الخاص بـ reCAPTCHA لاستخراج النطاق الذي يُربط به الرمز:
from bs4 import BeautifulSoup
from urllib.parse import urlparse
def extract_recaptcha_domain(html, page_url):
"""Extract the domain reCAPTCHA uses for token binding."""
soup = BeautifulSoup(html, "html.parser")
# Check for reCAPTCHA iframe
iframe = soup.find("iframe", src=lambda s: s and "recaptcha" in s)
if iframe:
src = iframe.get("src", "")
# The iframe URL may contain the domain parameter
if "domain=" in src:
# Extract domain from iframe URL
pass
# Default: use the page URL's domain
return urlparse(page_url).netloc
أداة تشخيص تطابق النطاق
تجمع الأداة التالية الفحوصات السابقة في مكان واحد:
- تتبع سلسلة إعادة التوجيه حتى النطاق النهائي.
- تقارن صيغتي www وغير www.
- تقترح قيمة
pageurlالصحيحة الجاهزة للاستخدام.
import requests
from urllib.parse import urlparse
class DomainDiagnostic:
"""Diagnose domain verification issues for reCAPTCHA solving."""
def __init__(self, target_url):
self.target_url = target_url
self.issues = []
def check_redirects(self):
"""Check if the URL redirects to a different domain."""
try:
response = requests.get(
self.target_url, allow_redirects=True, timeout=15,
headers={"User-Agent": "Mozilla/5.0 Chrome/120.0.0.0"},
)
final_url = response.url
original_domain = urlparse(self.target_url).netloc
final_domain = urlparse(final_url).netloc
if original_domain != final_domain:
self.issues.append({
"type": "redirect",
"message": f"Redirects from {original_domain} to {final_domain}",
"fix": f"Use pageurl: {final_url}",
})
return final_url
except Exception as e:
self.issues.append({"type": "error", "message": str(e)})
return self.target_url
def check_www_variant(self):
"""Check if www and non-www point to the same content."""
parsed = urlparse(self.target_url)
domain = parsed.netloc
if domain.startswith("www."):
alt_domain = domain[4:]
else:
alt_domain = f"www.{domain}"
alt_url = self.target_url.replace(domain, alt_domain)
try:
alt_response = requests.get(alt_url, allow_redirects=True, timeout=10)
alt_final = urlparse(alt_response.url).netloc
if alt_final != domain and alt_final != alt_domain:
self.issues.append({
"type": "www_redirect",
"message": f"{alt_domain} redirects to {alt_final}",
})
except Exception:
pass
def report(self):
"""Generate diagnostic report."""
final_url = self.check_redirects()
self.check_www_variant()
print(f"Target URL: {self.target_url}")
print(f"Final URL: {final_url}")
print(f"Use as pageurl: {final_url}")
if self.issues:
print("\nIssues found:")
for issue in self.issues:
print(f" [{issue['type']}] {issue['message']}")
if "fix" in issue:
print(f" Fix: {issue['fix']}")
else:
print("\nNo domain issues detected.")
# Usage
diag = DomainDiagnostic("https://example.com/login")
diag.report()
الأسئلة الشائعة
لماذا يُقبل الرمز في المتصفح ويُرفض عند إرساله من السكربت؟
عادةً لأن السكربت يرسل من أصل مختلف عن الصفحة التي تراها: المتصفح على النطاق النهائي بعد إعادة التوجيه بينما يمرّر السكربت النطاق الجذر. قارن شريط العنوان بقيمة pageurl واجعلهما متطابقين.
كيف أعرف النطاق الصحيح الذي أضعه في pageurl؟
اطلب صفحة الهدف واترك مكتبتك تتبع كل إعادات التوجيه، ثم استخدم عنوان الصفحة النهائي. أداة التشخيص هنا تفعل ذلك وتطبع القيمة الجاهزة في pageurl.
هل يؤثر اختلاف النطاق الفرعي على قبول الرمز؟
قد يؤثر، والسلوك يعتمد على إعداد مالك الموقع:
- مع حرف البدل (
*.example.com) تُقبل النطاقات الفرعية. - مع التحقق الصارم قد يُرفض رمز
sub.example.comعند إرساله إلىexample.com.
لذلك عامل كل نطاق فرعي كنطاق مستقل عند ضبط pageurl.
هل يضبط CaptchaAI تطابق النطاق نيابةً عني؟
لا. يولّد CaptchaAI رمزاً مربوطاً بقيمة pageurl التي تمرّرها أنت فقط، دون التحقق من إعدادات مالك الموقع. مسؤوليتك أن يطابق هذا العنوان النطاق الذي سيُرسَل إليه الرمز.
ما الفرق بين خطأ invalid-input-response وعدم تطابق اسم المضيف؟
الفرق في مصدر الرفض:
invalid-input-response: الرمز نفسه غير مقبول (منتهٍ أو مستهلَك أو من نطاق مختلف) — رفض من Google.- عدم تطابق اسم المضيف: يظهر مع
success: trueلكن حقلhostnameيشير إلى نطاق يرفضه منطق خادمك أنت.
الخلاصة
يربط reCAPTCHA كل رمز باسم المضيف الذي تولّد عليه، ويبقى عدم تطابق pageurl أكثر أسباب الفشل شيوعاً في الأتمتة: العنوان الذي تمرّره إلى CaptchaAI يجب أن يطابق النطاق الذي سيُرسَل إليه الرمز. ثلاث خطوات تحوّل أخطاء النطاق الصامتة إلى مشكلة قابلة للتتبع:
- اتبع سلسلة إعادة التوجيه للوصول إلى النطاق الفعلي.
- تعامل مع صيغتي www وغير www بحسب ما يعتمده الموقع.
- استعن بأداة التشخيص لكشف عدم التطابق قبل الحل.
مقالات ذات صلة
- كيفية حل رد نداء reCAPTCHA v2 عبر الـ API
- التعامل مع reCAPTCHA v2 وTurnstile على الموقع نفسه
- أخطاء صور الشبكة الشائعة في CAPTCHA وإصلاحها