Troubleshooting

reCAPTCHA Domain Verification Errors and Fixes

reCAPTCHA validates that tokens are used on the same domain they were generated for. When there is a mismatch between the CAPTCHA's domain configuration and the actual request origin, you get domain verification errors. These failures are silent — the token appears valid but the server rejects it. This guide covers every domain-related error scenario and how to fix it.


How domain verification works

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 }

Where domain is checked

Check point What is verified
Client-side Widget loads only on allowed domains (optional — can be disabled)
Token generation Hostname embedded in token matches page origin
Server validation siteverify returns the hostname — server should verify it matches

Common domain verification errors

Error 1: Hostname mismatch in siteverify response

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

The token is valid, but the hostname field shows a different domain than expected. Some server implementations reject this:

# 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

Causes:

  • Token solved for www.example.com but validated on example.com
  • Token solved for staging.example.com but validated on example.com
  • Proxy or CDN changes the apparent hostname

Fix: Ensure the pageurl in your solver request exactly matches the domain where the token will be submitted.

Error 2: Widget refuses to load

The reCAPTCHA widget shows an error or does not render:

ERROR: Invalid domain for site key

Causes:

  • The site key's allowed domains do not include the current page's domain
  • Loading the widget from localhost or file:// protocol
  • Using an IP address instead of a domain name

Fix for automation: This is a site-owner configuration issue. For solving, ensure you pass the correct pageurl that matches an allowed domain.

Error 3: Token rejected despite correct solve

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

The token was generated for a different domain than where it is being validated.

Common automation cause: The pageurl sent to the solver does not match the actual target domain:

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

Domain matching rules

Exact match vs wildcard

reCAPTCHA's domain verification is not strict subdomain matching by default. The behavior depends on the site owner's configuration:

Registered domain Acceptable origins
example.com example.com, www.example.com, sub.example.com (if wildcard enabled)
www.example.com www.example.com only (if strict)
*.example.com Any subdomain of example.com
localhost localhost only (for development)

Server-side hostname behavior

When validating via siteverify, the hostname in the response reflects the page where the token was generated. The site owner's server decides whether to accept it:

# 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

Fixing domain errors in automation

Fix 1: Match pageurl exactly

The most common fix — ensure pageurl matches the actual target:

# 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,
})

Fix 2: Handle www vs non-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

Fix 3: Detect the correct domain from redirect chain

Some sites redirect through multiple domains:

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

Fix 4: Extract domain from reCAPTCHA callback URL

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

Domain verification diagnostic tool

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()

Troubleshooting table

Symptom Likely cause Diagnostic Fix
Token always rejected pageurl doesn't match target domain Compare solver pageurl with actual submission domain Update pageurl to match
Works on www, fails on non-www Domain variant mismatch Check redirect behavior Use the variant the target site uses (follow redirects)
Works sometimes, fails sometimes CDN or load balancer serves different domains Check if domain varies per request Use a consistent URL from the redirect chain
Works in browser, fails in script Script sends from different origin Compare browser URL bar with script's pageurl Match the browser's final URL
Enterprise token rejected Wrong project or domain binding Verify Enterprise site key matches the domain Check Enterprise console domain settings

Frequently asked questions

Does CaptchaAI handle domain verification automatically?

CaptchaAI generates tokens bound to the pageurl you provide. You must ensure this URL matches the domain where you will submit the token. CaptchaAI does not verify domain configuration — it uses whatever pageurl you specify.

Can I solve a CAPTCHA for one domain and use it on another?

No. reCAPTCHA tokens are bound to the domain they were generated for. A token generated for example.com cannot be used on other-site.com. Even different subdomains may fail if the site validates the hostname strictly.

Why does my token work in testing but fail in production?

Common causes: (1) Testing uses localhost which has different domain rules, (2) Production uses a CDN with a different domain, (3) Production has stricter hostname validation, (4) Different URL path or redirect chain in production.

Does the path matter, or just the domain?

Only the domain (hostname) is validated by reCAPTCHA. The path (/login, /signup) does not affect domain verification. However, you should still use the correct full URL as pageurl because some solver implementations may use it for other purposes.


Summary

reCAPTCHA domain verification ties tokens to the hostname where they were generated. The most common automation failure is a pageurl mismatch — the URL passed to CaptchaAI must match the domain where the token will be submitted. Follow redirects to find the actual domain, handle www vs non-www variants, and use the domain diagnostic tool to identify mismatches before solving.

Discussions (0)

No comments yet.