Токен получен, success равен true, score — 0,9, а форма всё равно возвращает 403. В большинстве таких случаев виноват не решатель и не score, а параметр action: строка-метка, которую страница передаёт в grecaptcha.execute() и которая запекается внутрь токена.
Сервер сайта после проверки токена сравнивает пришедшее action с тем, которое он ожидает на этом маршруте. Не совпало — запрос отклоняется молча или с 403, независимо от того, насколько «человеческим» выглядит поведение. Поэтому при работе с reCAPTCHA v3 через API значение action нужно не угадывать, а вытаскивать из кода страницы.
Как action попадает в токен
Страница вызывает grecaptcha.execute() и передаёт строку действия — обычно это имя сценария: login, checkout, homepage, submit_form.
grecaptcha.execute('SITEKEY', { action: 'submit_form' });
Эта строка становится частью токена. Когда сервер отправляет токен на проверку в Google, в ответе возвращается то же самое значение:
{
"success": true,
"score": 0.9,
"action": "submit_form",
"challenge_ts": "2024-01-15T12:00:00Z",
"hostname": "example.com"
}
Дальше идёт та самая строка проверки: response.action === 'submit_form'. Если токен решён с action: 'homepage', он остаётся криптографически корректным, но условие не выполняется и запрос отбрасывается. Ключевой момент: action — это третье поле, по которому фильтрует сервер, после success и score. Разбор самого балла — отдельная тема, здесь речь только про совпадение строки действия.
Четыре сценария и что вернёт сервер
| Сценарий | Что происходит |
|---|---|
| Значение совпадает | Токен принят, запрос проходит |
| Значение не то | Токен валиден, но сервер отклоняет его из-за несовпадения action |
| Значение пустое | Часть сайтов принимает такой токен, часть — нет |
| Значение не найдено | Возьмите homepage как запасной вариант: не все сайты сверяют action |
Отсюда практический вывод для отладки: если 403 приходит стабильно при нормальном балле, сначала сверьте action и только потом трогайте прокси, задержки и сценарий поведения.
Где найти реальное значение action
Способ 1. DevTools, вкладка «Сеть»
- Откройте DevTools — вкладка Сеть.
- Отфильтруйте запросы по
recaptchaилиanchor. - Выполните действие, которое запускает проверку: отправьте форму или откройте нужный экран.
- Найдите вызовы
grecaptcha.executeв столбце «Инициатор». - Значение действия видно в параметрах вызова.
Способ 2. Поиск по исходному коду страницы
// In the browser console:
document.querySelectorAll('script').forEach(s => {
if (s.textContent.includes('action')) {
const match = s.textContent.match(/action['":\s]+['"](\w+)['"]/);
if (match) console.log('Found action:', match[1]);
}
});
Сниппет проходит по инлайновым скриптам и вытаскивает первое похожее значение. На сборках с минификацией он часто промахивается — тогда переходите к третьему способу.
Способ 3. Поиск по файлам JavaScript
- Откройте DevTools — вкладка Источники.
- Нажмите
Ctrl + Shift + Fдля поиска по всем файлам. - Ищите
grecaptcha.executeилиaction:. - Нужное значение — строка, переданная в объекте параметров.
Типовые значения, которые встречаются чаще всего
| Значение | Где применяется |
|---|---|
homepage |
Заход на главную или лендинг |
login |
Форма входа |
submit |
Отправка обычной формы |
register |
Регистрация аккаунта |
checkout |
Страница оплаты |
contact |
Форма обратной связи |
search |
Поисковые запросы |
Список полезен как ориентир, но не как замена проверке: на кастомных фронтендах встречаются значения вида web_login_v2 или order_step3, и угадать их невозможно.
Передача action в запросе к CaptchaAI
Параметр уходит вместе с googlekey и pageurl в in.php, а результат забирается опросом res.php. Ниже — минимальный рабочий вариант на Python.
Python
import requests
import time
response = requests.get("https://ocr.captchaai.com/in.php", params={
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"version": "v3",
"googlekey": "6LfZil0UAAAAADM1Dpz...",
"action": "login", # Must match the site's action
"pageurl": "https://staging.example.com/qa-login",
"json": 1
})
task_id = response.json()["request"]
for _ in range(30):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY", "action": "get", "id": task_id, "json": 1
}).json()
if result.get("status") == 1:
token = result["request"]
break
Обратите внимание на нюанс, на котором спотыкаются при первом подключении: слово action в этом API встречается дважды и в разных смыслах. В запросе к in.php это метка сценария reCAPTCHA v3, а в запросе к res.php — служебная команда get, то есть «отдай результат по ID задачи». Путать их не стоит, переименовывать — тем более: оба имени зафиксированы в API.
Node.js
const axios = require('axios');
async function solveRecaptchaV3(sitekey, pageurl, action) {
const submit = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: 'YOUR_API_KEY',
method: 'userrecaptcha',
version: 'v3',
googlekey: sitekey,
action: action,
pageurl: pageurl,
json: 1
}
});
const taskId = submit.data.request;
for (let i = 0; i < 30; i++) {
await new Promise(r => setTimeout(r, 5000));
const result = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
});
if (result.data.status === 1) return result.data.request;
}
throw new Error('Timeout waiting for solution');
}
solveRecaptchaV3('6LfZil0UAAAAADM1Dpz...', 'https://staging.example.com/qa-login', 'login')
.then(token => console.log('Token:', token));
В обеих версиях action вынесен в параметр функции или в переменную не случайно: у одного и того же сайта на разных экранах значения разные, и хардкод в одном месте рано или поздно сломает второй сценарий.
Практический пример: ночной регресс интернет-магазина
Типичная ситуация для команды из Алматы или Минска, которая гоняет ночной регресс на стенде в европейском регионе. В наборе три сценария: вход в личный кабинет, добавление товара и оформление заказа. Фронтенд ставит login на первом шаге и checkout на последнем, а тесты, написанные копипастой, во всех трёх запросах отправляют login.
Результат выглядит обманчиво: два теста зелёные, третий падает с 403 раз в несколько прогонов. Команда начинает искать плавающую проблему в сети или в тайм-аутах, хотя причина статична — на шаге оплаты сервер ждёт checkout.
Лечится это одной строкой: значение action привязывается к странице, а не к тестовому набору. Удобно хранить соответствие «URL → action» в конфиге рядом с фикстурами и подставлять его при вызове решателя.
По ресурсам такой прогон недорогой. Тарификация CaptchaAI идёт по числу одновременных потоков, а не по количеству решений: план BASIC ($15/мес, 5 потоков) закрывает регресс, где одновременно выполняется не больше пяти проверок, а STANDARD ($30/мес, 15 потоков) берут, когда сьюты запускаются параллельно на нескольких агентах. Предсказуемая месячная сумма в USD здесь удобнее оплаты за каждое решение: стоимость ночного прогона не зависит от того, сколько раз перезапустили упавший тест.
Короткий чек-лист перед выкаткой
- Значение
actionвзято из кода страницы, а не из общего списка. - Для каждого маршрута с проверкой заведено своё значение.
- Регистр совпадает посимвольно:
Loginиlogin— разные строки. - В логах решателя видно, какое именно значение ушло в
in.php. - Если сайт
actionне сверяет, в конфиге всё равно стоит явное значение, а не пустая строка.
Отдельная строка в чек-листе — фиксировать значение action в конфиге вместе с pageurl: так при переносе теста на другой стенд пара «страница + метка» переезжает целиком и не рассыпается.
Частые вопросы
Почему сервер возвращает 403, хотя балл высокий?
Скорее всего, не совпало action. Проверка на стороне сайта состоит минимум из трёх условий: success, порог по score и точное равенство строки действия. Высокий балл не спасает, если третье условие не выполнено.
Чувствителен ли action к регистру и можно ли использовать дефисы?
Сравнение строгое и посимвольное, поэтому регистр важен. Допустимы буквы, цифры, подчёркивания и слеши; дефисы и кириллицу лучше не использовать вовсе — передавайте ровно ту строку, которую видите в вызове grecaptcha.execute.
Что делать, если на сайте несколько разных значений action?
Заведите соответствие «страница → значение» и подставляйте его динамически. Один захардкоженный вариант неизбежно упрётся в сценарий, где ожидается другая метка.
Как определить action в SPA?
Одностраничные приложения вызывают grecaptcha.execute динамически, часто уже после первичной отрисовки. Поставьте точку останова на grecaptcha.execute в DevTools или следите за XHR-запросами — так видно и момент вызова, и переданную строку.
Сколько потоков нужно для reCAPTCHA v3 в CI?
Считайте по числу одновременных проверок, а не по общему количеству за ночь. Пяти потоков плана BASIC ($15/мес) хватает последовательному прогону; при параллельном запуске сьютов на нескольких агентах берут STANDARD ($30/мес, 15 потоков).