Токен Cloudflare Turnstile, который работает при тестировании, терпит неудачу в производстве. Причина: истечение срока действия токена. токены Cloudflare Turnstile имеют ограниченный срок действия, и если ваш рабочий процесс занимает слишком много времени между получением токена и его отправкой, сайт отклоняет его. Вот как правильно обращаться с таймингом.
Срок действия токена
Срок действия токенов Cloudflare Turnstile истекает примерно через 300 секунд (5 минут) после создания. Это больше, чем ~120 секунд в reCAPTCHA, но в сложных рабочих процессах все равно возникают условия гонки.
| Тип капчи | Срок действия токена |
|---|---|
| reCAPTCHA v2/v3 | ~120 секунд |
| Cloudflare Turnstile | ~300 секунд |
| hCaptcha | ~120 секунд |
Таймер запускается, когда Cloudflare генерирует токен, а не тогда, когда CaptchaAI возвращает его вам, и не тогда, когда вы получаете его в своем коде.
Состояние гонки
Time 0:00 — You submit a Turnstile task to CaptchaAI
Time 0:15 — CaptchaAI begins solving
Time 0:20 — Token is generated (timer starts here)
Time 0:25 — CaptchaAI returns token to you
Time 0:25+ — Your code processes the token
Time ??? — Your code submits the token to the site
Часы отсчитывают время 0:20. У вас есть время примерно до 5:20, чтобы отправить токен. Звучит щедро, но подумайте, что происходит в реальном рабочем процессе:
Time 0:20 — Token generated
Time 0:25 — Received by your code
Time 0:30 — Fill form fields
Time 0:35 — Navigate to next page
Time 1:00 — Handle additional dialogs
Time 2:00 — Wait for page load
Time 4:00 — Network latency spike
Time 5:30 — Submit token → EXPIRED
Распространенные сценарии гонок
1. Многошаговые формы
Формы, требующие несколько страниц перед окончательной отправкой:
Step 1: Fill personal info → Step 2: Fill address →
Step 3: Solve CAPTCHA → Step 4: Review → Step 5: Submit
Если CAPTCHA находится на шаге 3, а отправка происходит на шаге 5, задержка между решением и отправкой может превышать 5 минут.
2. Очереди пакетной обработки
предварительная QA-проверка токенов и их использование позже:
# DON'T: Solve all tokens first, then use them
tokens = []
for url in urls:
tokens.append(solve_turnstile(url)) # Tokens age while waiting
for url, token in zip(urls, tokens):
submit_form(url, token) # Early tokens may be expired
3. Циклы повторов со старыми токенами
Повторное использование токена после неудачной отправки:
token = solve_turnstile(site_key, page_url)
for attempt in range(3):
result = submit_form(page_url, token)
if result.ok:
break
# BUG: Retrying with the same token — it may be expired OR already consumed
Предотвращение истечения срока действия
Стратегия 1. Решение проблем «точно в срок»
Запрашивайте токен только тогда, когда вы готовы его отправить:
import requests
import time
def solve_turnstile(site_key, page_url):
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": "YOUR_API_KEY",
"method": "turnstile",
"sitekey": site_key,
"pageurl": page_url,
"json": 1
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(3)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": "YOUR_API_KEY",
"action": "get",
"id": task_id,
"json": 1
})
data = result.json()
if data["status"] == 1:
return data["request"]
raise TimeoutError("Solve timed out")
# Complete all form steps FIRST
fill_personal_info()
fill_address()
navigate_to_review()
# THEN solve and submit immediately
token = solve_turnstile(site_key, page_url)
submit_form(token) # Submit within seconds of receiving the token
Стратегия 2: Отслеживание возраста токена
import time
class TimedToken:
def __init__(self, token, created_at=None):
self.token = token
self.created_at = created_at or time.time()
self.max_age = 270 # 4.5 min — safety margin from 5 min limit
@property
def is_valid(self):
return (time.time() - self.created_at) < self.max_age
@property
def remaining_seconds(self):
return max(0, self.max_age - (time.time() - self.created_at))
# Usage
timed_token = TimedToken(solve_turnstile(site_key, page_url))
# Check before using
if timed_token.is_valid:
submit_form(timed_token.token)
else:
# Solve a fresh token
timed_token = TimedToken(solve_turnstile(site_key, page_url))
submit_form(timed_token.token)
Стратегия 3: Свежий токен при повторной попытке (JavaScript)
async function submitWithFreshToken(siteKey, pageUrl, formData) {
const maxRetries = 3;
for (let attempt = 0; attempt < maxRetries; attempt++) {
// Always solve a fresh token for each attempt
const token = await solveTurnstile(siteKey, pageUrl);
const response = await fetch(pageUrl, {
method: 'POST',
body: JSON.stringify({ ...formData, 'cf-turnstile-response': token }),
headers: { 'Content-Type': 'application/json' }
});
if (response.ok) return await response.json();
console.log(`Attempt ${attempt + 1} failed, solving fresh token...`);
}
throw new Error('All attempts failed');
}
Обнаружение токенов с истекшим сроком действия
Сайт обычно не сообщает вам явно о том, что срок действия токена истек. Ищите эти сигналы:
| Сигнал | Индикация |
|---|---|
| HTTP 403 после отправки токена | Токен недействителен или срок его действия истек. |
| Перенаправление обратно на страницу формы | Проверка токена не удалась |
| Сообщение об ошибке: «Проверка не удалась». | Общий сбой — возможно, истек срок действия |
| Страница испытания появляется снова | Токен отклонен, Cloudflare повторно бросает вызов |
Регистрация для диагностики
import time
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("turnstile")
token_received_at = time.time()
token = solve_turnstile(site_key, page_url)
logger.info(f"Token received, length: {len(token)}")
# ... workflow steps ...
submit_time = time.time()
age = submit_time - token_received_at
logger.info(f"Submitting token, age: {age:.1f}s")
if age > 270:
logger.warning(f"Token may be expired (age: {age:.1f}s > 270s safety limit)")
Поведение автоматического обновления Cloudflare Turnstile
В потоках на основе браузера виджеты Turnstile автоматически обновляют токены до истечения срока их действия. data-expired-callback срабатывает, когда истекает срок действия токена:
turnstile.render('#captcha', {
sitekey: '0x4AAAA...',
callback: (token) => {
console.log('New token:', token);
},
'expired-callback': () => {
console.log('Token expired — widget will auto-refresh');
}
});
При автоматизации только с помощью API (без браузера) автоматическое обновление не дает преимуществ. Вы должны сами управлять свежестью токенов.
Поиск неисправностей
| Проблема | Причина | Исправить |
Следующие шаги
- CaptchaAI Quickstart: ваше первое решение CAPTCHA за 5 минут
- Как решить reCAPTCHA v2 через API: пошаговое руководство
- Как решить Cloudflare Turnstile через API
- Как решить GeeTest v3 с помощью API