reCAPTCHA 验证令牌是否在生成令牌的同一域上使用。当验证码的域配置与实际请求来源不匹配时,您会收到域验证错误。这些失败是无声的——令牌看似有效,但服务器拒绝了它。本指南涵盖了每个与域相关的错误场景以及如何修复它。
域验证的工作原理
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 返回主机名 - 服务器应验证它是否匹配 |
常见域验证错误
错误 1:siteverify 响应中的主机名不匹配
{
"success": true,
"hostname": "subdomain.example.com",
"challenge_ts": "2025-01-15T10:30:00Z"
}
令牌有效,但 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 与将提交令牌的域完全匹配。
错误2:小部件拒绝加载
reCAPTCHA 小部件显示错误或不呈现:
ERROR: Invalid domain for site key
原因:
- 站点密钥允许的域不包括当前页面的域
- 从本地主机或文件加载小部件:// 协议
- 使用 IP 地址代替域名
自动化修复: 这是站点所有者配置问题。为了解决这个问题,请确保您传递与允许的域匹配的正确 pageurl。
错误 3:尽管解决正确,但令牌仍被拒绝
{
"success": false,
"error-codes": ["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://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!
域名匹配规则
精确匹配与通配符
默认情况下,reCAPTCHA 的域验证不是严格的子域匹配。该行为取决于网站所有者的配置:
| 注册域名 | 可接受的来源 |
|---|---|
example.com |
example.com、www.example.com、sub.example.com(如果启用通配符) |
www.example.com |
仅 www.example.com(如果严格) |
*.example.com |
example.com 的任何子域 |
localhost |
仅localhost(用于开发) |
服务器端主机名行为
通过 siteverify 进行验证时,响应中的 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
修复自动化中的域错误
修复 1:完全匹配 pageurl
最常见的修复方法 - 确保 pageurl 与实际目标匹配:
# 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,
})
修复 2:处理 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://staging.example.com/qa-login", allow_redirects=True)
actual_url = response.url # May be https://www.staging.example.com/qa-login after redirect
修复 3:从重定向链中检测正确的域
有些网站通过多个域重定向:
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
修复 4:从 reCAPTCHA 回调 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
域验证诊断工具
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()
故障排除表
| 症状 | 可能的原因 | 诊断 | 处理方式 |
|---|---|---|---|
| 令牌总是被拒绝 | pageurl 与目标域不匹配 | 将求解器 pageurl 与实际提交域进行比较 | 更新 pageurl 以匹配 |
| 在 www 上有效,在非 www 上失败 | 域变体不匹配 | 检查重定向行为 | 使用目标站点使用的变体(遵循重定向) |
| 有时有效,有时失败 | CDN 或负载均衡器服务于不同的域 | 检查域是否因请求而异 | 使用重定向链中一致的 URL |
| 在浏览器中有效,在脚本中失败 | 脚本从不同来源发送 | 将浏览器 URL 栏与脚本的 pageurl 进行比较 | 匹配浏览器的最终 URL |
| 企业令牌被拒绝 | 项目或域绑定错误 | 验证企业站点密钥与域匹配 | 检查企业控制台域设置 |
常见问题
CaptchaAI 是否自动处理域验证?
CaptchaAI 生成绑定到您提供的 pageurl 的令牌。您必须确保此 URL 与您将提交令牌的域匹配。 CaptchaAI 不验证域配置 - 它使用您指定的任何 pageurl。
我可以解决一个域的验证码并将其用于另一个域吗?
不会。reCAPTCHA 令牌绑定到为其生成的域。为 example.com 生成的令牌不能在 other-site.com 上使用。如果站点严格验证主机名,即使不同的子域也可能会失败。
为什么我的代币在测试中有效但在生产中失败?
常见原因:(1) 测试使用具有不同域规则的 localhost,(2) 生产使用具有不同域的 CDN,(3) 生产具有更严格的主机名验证,(4) 生产中的 URL 路径或重定向链不同。
路径重要还是域重要?
reCAPTCHA 仅验证域(主机名)。路径(/login、/signup)不会影响域验证。但是,您仍应使用正确的完整 URL 作为 pageurl,因为某些解算器实现可能会将其用于其他目的。
概括
reCAPTCHA 域验证将令牌与生成令牌的主机名联系起来。最常见的自动化故障是 pageurl 不匹配 — 传递给的 URLCaptchaAI必须与将提交令牌的域匹配。按照重定向查找实际域,处理 www 与非 www 变体,并在解决之前使用域诊断工具识别不匹配。
相关文章
- 如何使用Api解决Recaptcha V2回调
- Recaptcha V2 Turnstile同一站点处理
- 常见的网格图像验证码错误和修复