Troubleshooting

reCAPTCHA Alan Adı Doğrulama Hataları ve Düzeltmeleri

Token elinizde, çözüm başarılı görünüyor, ama hedef site isteği yine de reddediyor. Bunun neredeyse tek bir nedeni var: çözücüye verdiğiniz pageurl, token'ı gönderdiğiniz alan adıyla birebir aynı değil.

reCAPTCHA her token'ın içine üretildiği ana makine adını gömer. Sunucu siteverify yanıtındaki hostname değerini beklediğiyle karşılaştırır ve tutmuyorsa reddeder.


Önce şu dört şeyi kontrol edin

Kod açmadan önce bu listeyi geçin:

  1. Tarayıcıdaki nihai URL nedir? Adres çubuğundakini kopyalayın; pageurl o olmalı.
  2. www var mı yok mu? example.com ile www.example.com ayrı ana makine adı olabilir.
  3. Token'ı hangi hosta gönderiyorsunuz? Çözüm isteğiyle gönderim adresi ayrışmışsa uyuşmazlık kaçınılmazdır.
  4. Araya CDN veya reverse proxy giriyor mu? Host başlığını değiştiren katman hostname değerini de değiştirir.

Bu hatalar sessizdir: ne konsolda uyarı ne de 5xx yanıtı görürsünüz.


reCAPTCHA alan adı doğrulaması zincirin neresinde çalışır?

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 }

Alan adı üç ayrı noktada denetlenir

Kontrol noktası Ne doğrulanır
İstemci tarafı Widget yalnızca izin verilen alan adlarında yüklenir (kapatılabilir)
Token üretimi Token'ın içine gömülen ana makine adı, sayfanın kökeniyle eşleşir
Sunucu doğrulaması siteverify ana makine adını döndürür; kararı site sahibinin sunucusu verir

Kritik nokta: ret kararını Google değil, hedef sitenin doğrulama kodu verir.


Sık görülen üç alan adı hatası

Hata 1: siteverify yanıtındaki hostname tutmuyor

{
    "success": true,
    "hostname": "subdomain.example.com",
    "challenge_ts": "2025-01-15T10:30:00Z"
}

Token geçerli (success: true) ama hostname beklenenden farklı; katı sunucular reddeder:

# 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

Uyuşmazlık genelde şu üç kalıptan biridir:

Token nerede üretildi Doğrulama nerede yapıldı
www.example.com example.com
staging.example.com üretim alan adı
CDN arkasındaki gerçek host proxy'nin değiştirdiği Host

Düzeltme: pageurl ile gönderim adresi karakter karakter aynı olmalı — şema ve alt alan adı dahil.

Hata 2: Widget "Invalid domain for site key" hatası veriyor

Widget hiç görünmüyor ya da konsola şu satırı düşüyor:

ERROR: Invalid domain for site key
Yapılandırma durumu Sonuç
sitekey izinli listesinde o alan adı yok Widget hiç yüklenmez
Sayfa localhost ya da file:// üzerinden açık Köken doğrulanamaz
Alan adı yerine IP adresi kullanılmış Kayıtla eşleşmez

Otomasyon tarafındaki karşılığı: Bu, site sahibinin konsol ayarıdır; dışarıdan düzeltilmez. İzinli listeyle örtüşen pageurl gönderin.

Hata 3: Çözüm doğru ama token reddediliyor

{
    "success": false,
    "error-codes": ["invalid-input-response"]
}

Token başka bir alan adı için üretilmiş demektir; en sık görülen hâli, çözücüye giden pageurl ile isteğin gittiği adresin ayrışmasıdır:

# 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://staging.example.com/qa-login",  # ← Must match actual domain
    "json": 1,
})

# But submitting token to:
requests.post("https://app.staging.example.com/qa-login", ...)  # Different subdomain!

Belirtiden nedene: hızlı eşleme tablosu

Hata mesajı yoksa belirtiden geriye yürüyün:

Belirti Muhtemel neden Düzeltme
Token her seferinde reddediliyor pageurl hedef alan adıyla eşleşmiyor pageurl değerini gerçek hedefle eşitleyin
www'de çalışıyor, www'siz sürümde düşüyor Alan adı varyantı uyuşmazlığı Sitenin fiilen kullandığı varyantı kullanın
Bazen geçiyor, bazen geçmiyor CDN farklı alan adları sunuyor Zincirin sonundaki tek URL'de sabitleyin
Tarayıcıda sorun yok, betikte var Betik farklı bir kökenden gönderiyor Tarayıcının nihai URL'sini birebir kopyalayın
Enterprise token'ı reddediliyor Yanlış proje veya alan adı bağlama Enterprise konsolundaki alan adı ayarlarını gözden geçirin

Pratikte nasıl görünüyor: bir e-ticaret QA senaryosu

Türkiye'de otomasyon işi ağırlıkla e-ticaret ve fintek tarafında; regresyon testleri staging'de koşar, üretim ayrı alan adındadır. Haftalardır sorunsuz çalışan senaryo, üretime alındığı gün token reddedilmeye başlar:

Ortam Testin gönderdiği pageurl Sonuç
Staging https://staging.example.com/qa-login Token kabul edilir
Üretim https://staging.example.com/qa-login hostname uyuşmazlığı

Çözüm basit: adresi koda gömmeyin, ortam değişkeninden okuyun (CAPTCHA_PAGE_URL).

  • KVKK. Gerçek kullanıcı verisi KVKK yükümlülüğü doğurur; testleri sentetik veriyle (+90 formatında numaralar, tr-TR locale, Europe/Istanbul) yürütün.
  • Maliyet. CaptchaAI planları thread üzerinden faturalanır, fiyatlar USD: BASIC ($15/ay, 5 thread) küçük bir QA paketine yeter, paralel koşan ekipler ADVANCE ($90/ay, 50 thread) kullanır.

Alan adı eşleşme kuralları: tam eşleşme, joker ve alt alan adları

reCAPTCHA varsayılan olarak katı alt alan adı eşleştirmesi yapmaz; davranışı konsoldaki liste belirler:

Kayıtlı alan adı Kabul edilen kökenler
example.com example.com, www.example.com, sub.example.com (joker etkinse)
www.example.com Yalnızca www.example.com (katı yapılandırmada)
*.example.com example.com altındaki tüm alt alan adları
localhost Yalnızca localhost (geliştirme ortamı için)

Kabul kararını sunucu verir

siteverify yanıtındaki hostname token'ın üretildiği sayfayı yansıtır.

Kabul kararı sunucuya kalmıştır; aşağıdaki iki uygulamanın farkı, aynı token'ın neden bir yerde geçip başka yerde düştüğünü açıklar:

# 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

Otomasyon tarafında dört düzeltme

Düzeltme 1: pageurl'yi hedefle birebir eşitleyin

pageurl, token'ı göndereceğiniz adresin aynısı olmalı.

Şema, alt alan adı ve www varyantı dahil:

# Correct: pageurl matches where you'll submit the token
target_url = "https://www.staging.example.com/qa-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,
})

Düzeltme 2: www ve www'siz varyantı tek kurala bağlayın

Kanonik varyantı tahmin etmeyin; tek istek gerçek adresi verir:

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://staging.example.com/qa-login", allow_redirects=True)
actual_url = response.url  # May be https://www.staging.example.com/qa-login after redirect

Düzeltme 3: Yönlendirme zincirinin sonundaki alan adını kullanın

Bazı akışlar oturum açma adımını ayrı bir alan adına taşır:

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://staging.example.com/qa-login → https://auth.staging.example.com/qa-login
final_url = get_final_url("https://staging.example.com/qa-login")
# Use final_url as pageurl for solver

Düzeltme 4: Alan adını widget'ın kendisinden okuyun

Sayfa kaynağındaki reCAPTCHA iframe'i en doğrudan ipucudur:

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

Hazır teşhis betiği

Dört kontrolü tek sınıfta toplayın. Betik sırayla:

  1. Yönlendirme zincirini sonuna kadar izler.
  2. www ve www'siz varyantı ayrı ayrı dener.
  3. Kullanmanız gereken pageurl değerini yazdırır.
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://staging.example.com/qa-login")
diag.report()

Sık sorulan sorular

pageurl alanına tam URL mi yoksa yalnızca alan adı mı yazmalıyım?

Tam URL yazın. Doğrulanan yalnızca ana makine adıdır, ama tam adres sayfanın doğru yüklenmesini kolaylaştırır.

Sayfa yönlendirme yapıyorsa hangi adresi kullanmalıyım?

Zincirin sonundakini: requests.get(url, allow_redirects=True) çağrısının döndürdüğü response.url doğru sayfadır.

www'li ve www'siz sürümler için ayrı token gerekir mi?

Site katı doğrulama yapıyorsa evet; bunlar iki ayrı ana makine adıdır. Kanonik varyantı bir kez tespit edip akışı ona sabitleyin.

Aynı token'ı ikinci bir istekte yeniden kullanabilir miyim?

Hayır; token'lar tek kullanımlık ve kısa ömürlüdür. Yeniden denemede aynı token'ı göndermeyin, yeni bir çözüm isteği açın.

CaptchaAI hCaptcha alan adı hatalarını da çözüyor mu?

Hayır, hCaptcha desteklenmiyor. Desteklenen türler reCAPTCHA v2/v3, reCAPTCHA Enterprise, Cloudflare Turnstile ve GeeTest v3'tür; CaptchaFox (beta), Friendly Captcha (beta) ve Lemin (beta) beta aşamasındadır.


Özet

Alan adı hatalarının kaynağı hep aynıdır: pageurl ile token'ın gönderildiği adres tutmaz. Üç alışkanlık bu hatayı kapatır:

  • Yönlendirme zincirinin sonundaki adresi kullanın.
  • www varyantını bir kez netleştirip sabitleyin.
  • pageurl değerini ortam değişkeninden okuyun.

CaptchaAI tarafında ek yapılandırma yoktur; token verdiğiniz pageurl'ye bağlanır.

İlgili makaleler

Bu makale için yorumlar devre dışı bırakılmıştır.