Certains sites Web implémentent à la fois reCAPTCHA v2 et v3 sur la même page. Le schéma typique est le suivant : la v3 s'exécute de manière invisible en arrière-plan, et si le score est trop faible, la v2 apparaît comme un défi de secours visible. Cette double implémentation crée une confusion pour l'automatisation car vous devez gérer deux types CAPTCHA différents avec des méthodes de résolution différentes. Ce guide couvre la détection, les stratégies de résolution et les cas extrêmes courants.
Pourquoi les sites utilisent à la fois la v2 et la v3
User visits page
↓
reCAPTCHA v3 runs invisibly in background
↓
Score returned to server (e.g., 0.4)
↓
Score below threshold (e.g., < 0.7)?
├─ YES → Show reCAPTCHA v2 checkbox/image challenge
└─ NO → Allow action without visible CAPTCHA
Ce modèle offre le meilleur des deux mondes :
- La plupart des utilisateurs (score v3 élevé) ne voient aucun CAPTCHA → faible friction
- Utilisateurs suspects (score v3 faible) voir le défi v2 → solution de sécurité
- L'opérateur du site Web contrôle le seuil entre invisible et visible
Modèles de mise en œuvre double
Modèle 1 : pré-évaluation v3 + repli v2
Le modèle le plus courant. La v3 s'exécute en premier et la v2 n'apparaît qu'en cas de besoin.
<!-- Both scripts loaded -->
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
<form id="loginForm">
<!-- v2 widget (hidden initially) -->
<div id="recaptcha-v2-container" style="display:none;">
<div class="g-recaptcha" data-sitekey="V2_SITE_KEY"></div>
</div>
<button type="submit">Login</button>
</form>
<script>
// First attempt: v3 invisible
grecaptcha.ready(function() {
grecaptcha.execute('V3_SITE_KEY', {action: 'login'}).then(function(v3Token) {
fetch('/api/verify-v3', {
method: 'POST',
body: JSON.stringify({token: v3Token})
})
.then(r => r.json())
.then(data => {
if (data.score < 0.7) {
// Score too low → show v2 fallback
document.getElementById('recaptcha-v2-container').style.display = 'block';
grecaptcha.render('recaptcha-v2-container', {sitekey: 'V2_SITE_KEY'});
} else {
// Score OK → submit form directly
document.getElementById('loginForm').submit();
}
});
});
});
</script>
Modèle 2 : différentes clés de site pour différentes actions
Certains sites utilisent la version 3 pour la surveillance passive et la version 2 pour des actions spécifiques à haut risque :
Homepage → v3 only (passive score)
Login page → v3 assessment, v2 fallback
Checkout → v2 always (high security)
Contact form → v3 only
Modèle 3 : Script unique, mode double
Google prend en charge le chargement d'un seul script reCAPTCHA qui gère à la fois les versions 2 et 3 :
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script>
// v3 execute
grecaptcha.execute('V3_SITE_KEY', {action: 'login'});
// v2 render (uses a different site key)
grecaptcha.render('v2-container', {sitekey: 'V2_SITE_KEY'});
</script>
Détection de la double implémentation
Détection Python
import requests
import re
def detect_dual_recaptcha(url):
"""Detect if a page uses both reCAPTCHA v2 and v3."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
}
html = requests.get(url, headers=headers, timeout=15).text
result = {
"has_v3": False,
"has_v2": False,
"v3_site_key": None,
"v2_site_key": None,
"dual": False,
"pattern": None,
}
# Detect v3 (render parameter or enterprise.execute)
v3_match = re.search(r"api\.js\?render=([A-Za-z0-9_-]+)", html)
if v3_match and v3_match.group(1) != "explicit":
result["has_v3"] = True
result["v3_site_key"] = v3_match.group(1)
# Detect v3 in execute calls
v3_execute = re.search(
r"grecaptcha\.(?:enterprise\.)?execute\s*\(\s*['\"]([^'\"]+)['\"]",
html,
)
if v3_execute:
result["has_v3"] = True
if not result["v3_site_key"]:
result["v3_site_key"] = v3_execute.group(1)
# Detect v2 (g-recaptcha class or explicit render)
v2_match = re.search(r'data-sitekey="([^"]+)"', html)
if v2_match:
key = v2_match.group(1)
if key != result.get("v3_site_key"):
result["has_v2"] = True
result["v2_site_key"] = key
# Check for explicit v2 render
v2_render = re.search(
r"grecaptcha\.render\s*\([^,]+,\s*\{[^}]*sitekey:\s*['\"]([^'\"]+)",
html,
)
if v2_render:
result["has_v2"] = True
if not result["v2_site_key"]:
result["v2_site_key"] = v2_render.group(1)
result["dual"] = result["has_v3"] and result["has_v2"]
if result["dual"]:
# Determine pattern
if "display:none" in html or "display: none" in html:
result["pattern"] = "v3_pre_assessment_v2_fallback"
else:
result["pattern"] = "v2_v3_simultaneous"
return result
detection = detect_dual_recaptcha("https://example.com/login")
print(detection)
Détection Node.js
const axios = require("axios");
async function detectDualRecaptcha(url) {
const { data: html } = await axios.get(url, { timeout: 15000 });
const result = {
hasV3: false,
hasV2: false,
v3SiteKey: null,
v2SiteKey: null,
dual: false,
};
// v3 detection
const v3Match = html.match(/api\.js\?render=([A-Za-z0-9_-]+)/);
if (v3Match && v3Match[1] !== "explicit") {
result.hasV3 = true;
result.v3SiteKey = v3Match[1];
}
// v2 detection
const v2Match = html.match(/data-sitekey="([^"]+)"/);
if (v2Match && v2Match[1] !== result.v3SiteKey) {
result.hasV2 = true;
result.v2SiteKey = v2Match[1];
}
result.dual = result.hasV3 && result.hasV2;
return result;
}
detectDualRecaptcha("https://example.com/login").then(console.log);
Stratégies de résolution pour le double reCAPTCHA
Stratégie 1 : Résolvez d'abord la v3, puis la v2 si nécessaire
La stratégie optimale reflète le flux propre du site :
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_v3(site_key, page_url, action="login"):
"""Solve reCAPTCHA v3 and return token."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"version": "v3",
"action": action,
"json": 1,
}).json()
task_id = submit["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("v3 solve timeout")
def solve_v2(site_key, page_url):
"""Solve reCAPTCHA v2 and return token."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": site_key,
"pageurl": page_url,
"json": 1,
}).json()
task_id = submit["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("v2 solve timeout")
def solve_dual_recaptcha(v3_key, v2_key, page_url, action="login"):
"""Handle dual reCAPTCHA: try v3, fall back to v2."""
# Step 1: Try v3
v3_token = solve_v3(v3_key, page_url, action)
# Step 2: Submit v3 token to target
response = requests.post(f"{page_url}/verify", data={
"g-recaptcha-response": v3_token,
})
# Step 3: Check if v2 fallback is needed
if "recaptcha" in response.text.lower() and v2_key:
print("v3 score too low — v2 fallback triggered")
v2_token = solve_v2(v2_key, page_url)
return {"version": "v2", "token": v2_token}
return {"version": "v3", "token": v3_token}
result = solve_dual_recaptcha(
v3_key="6LcExample_v3_key",
v2_key="6LcExample_v2_key",
page_url="https://example.com/login",
)
print(f"Solved with {result['version']}")
Stratégie 2 : ignorer la v3, résoudre directement la v2
Si vous savez que le site affiche toujours la v2 pour le trafic automatisé (le score de la v3 sera faible), ignorez la v3 et résolvez immédiatement la v2 :
# If you consistently fail v3 assessment, just solve v2 directly
token = solve_v2(v2_site_key, page_url)
submit_form(token)
Cela permet d'économiser le temps et le coût d'une résolution v3 qui pourrait ne pas dépasser le seuil.
Stratégie 3 : gestion basée sur le navigateur
Pour les implémentations complexes, utilisez un navigateur pour gérer le flux de secours :
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver = webdriver.Chrome()
driver.get("https://example.com/login")
time.sleep(3)
# Check if v2 widget is visible
v2_visible = driver.execute_script("""
const container = document.querySelector('.g-recaptcha');
if (!container) return false;
const style = window.getComputedStyle(container.parentElement);
return style.display !== 'none' && style.visibility !== 'hidden';
""")
if v2_visible:
# v2 is showing — solve v2
sitekey = driver.find_element(
By.CSS_SELECTOR, "[data-sitekey]"
).get_attribute("data-sitekey")
token = solve_v2(sitekey, driver.current_url)
driver.execute_script(
f'document.getElementById("g-recaptcha-response").value = "{token}";'
)
else:
# v3 only — solve v3
# Extract v3 key from page source
v3_key = driver.execute_script(
"return document.querySelector('script[src*=\"render=\"]')"
".src.match(/render=([^&]+)/)[1];"
)
token = solve_v3(v3_key, driver.current_url)
# Inject v3 token into the form
driver.execute_script(f"""
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'g-recaptcha-response';
input.value = '{token}';
document.querySelector('form').appendChild(input);
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
Cas extrêmes
Deux clés de site différentes sur la même page
Les sites utilisant le double reCAPTCHA auront DEUX clés de site différentes : une pour la v3 et une pour la v2. La clé v3 apparaît dans l'URL du script ?render=KEY et dans grecaptcha.execute('KEY', ...). La clé v2 apparaît dans data-sitekey="KEY" sur le widget div. L’utilisation d’une mauvaise clé pour une mauvaise version produira des jetons invalides.
reCAPTCHA Enterprise avec solution de secours v2
Certaines implémentations Enterprise utilisent la v3 Enterprise pour la notation et la v2 pour les défis :
# Detect and handle Enterprise + v2 combo
if "recaptcha/enterprise.js" in html:
# Use enterprise parameter for v3
v3_params = {"enterprise": 1, "version": "v3"}
else:
v3_params = {"version": "v3"}
Plusieurs formulaires sur une seule page
Si une page comporte plusieurs formulaires (connexion + inscription), chacun peut avoir sa propre instance reCAPTCHA. Extrayez la clé du site du formulaire spécifique que vous ciblez :
# Target the login form specifically
login_form = soup.find("form", id="login-form")
widget = login_form.find(attrs={"data-sitekey": True})
sitekey = widget["data-sitekey"]
Questions fréquemment posées
Dois-je résoudre les versions v2 et v3 sur la même page ?
Non. En règle générale, vous résolvez d'abord la v3 (elle s'exécute automatiquement). Si le score v3 dépasse le seuil du site, aucun défi v2 n'apparaît et vous avez terminé. Vous n'avez besoin de résoudre la v2 que si le score v3 déclenche le repli.
Puis-je utiliser un seul appel API CaptchaAI pour un double reCAPTCHA ?
Les numéros v2 et v3 sont des types CAPTCHA distincts avec des clés de site et des méthodes de résolution différentes. Chacun nécessite son propre appel API à CaptchaAI. Cependant, vous ne devez effectuer qu'un seul appel si la v3 passe sans déclencher la v2.
Comment savoir si le repli v2 a été déclenché ?
Vérifiez la réponse du serveur après avoir soumis le jeton v3. Si la réponse contient un widget HTML v2 ou déclenche un challenge v2 (redirection ou réponse AJAX avec CAPTCHA HTML), le repli a été déclenché. Dans un navigateur, vérifiez si le conteneur v2 devient visible après la soumission de la v3.
Quelle clé de site dois-je utiliser pour chaque version ?
La clé du site v3 se trouve dans l'URL du script : api.js?render=V3_KEY. La clé du site v2 se trouve dans le widget HTML : data-sitekey="V2_KEY". Ce sont toujours des clés différentes.
Résumé
Les implémentations doubles de reCAPTCHA utilisent la version 3 pour une pré-évaluation invisible et la version 2 comme solution de repli visible lorsque le score de la version v3 est trop faible. Détectez les deux versions en vérifiant le paramètre de rendu (v3) et la clé de site de données du widget (v2). La stratégie d'automatisation optimale est la suivante : résolvez d'abord la v3 avecCaptchaAI, soumettez le jeton et résolvez la v2 uniquement si la solution de repli se déclenche. Chaque version nécessite un appel API distinct avec sa propre clé de site.
Articles connexes
- Dynamique d'application Recaptcha à page unique
- Comment résoudre le rappel Recaptcha V2 à l'aide de l'API
- Gestion du tourniquet Recaptcha V2 sur le même site