Troubleshooting

Token Turnstile Tidak Valid Setelah Solve: Diagnosis dan Perbaikan

Umur token Turnstile hanya sekitar 300 detik, dan itulah alasan nomor satu token yang sudah berhasil diselesaikan tetap ditolak situs. Intinya: yang bermasalah bukan hasil solve-nya, melainkan jarak waktu antara token diterima dan token dikirim — ditambah tiga konteks yang harus cocok, yaitu sitekey, nama field, dan cookie sesi.

Mulai dari log, bukan dari kode: catat selisih detik antara respons res.php dan request POST Anda. Di bawah sepuluh detik tetapi tetap 403? Masa berlaku sudah bisa dicoret.

Enam penyebab di bawah diurutkan dari yang paling sering muncul sampai yang paling jarang, masing-masing dengan satu cara verifikasi yang selesai dalam hitungan menit.

Gejala: kenapa token Turnstile ditolak padahal solve berhasil

Setiap gejala di tabel ini mengarah ke satu penyebab dominan. Cocokkan dulu apa yang Anda lihat, baru buka bagian penyebabnya.

Yang Anda lihat Penyebab paling mungkin
Situs membalas 403 begitu token dikirim Token sudah lewat masa berlaku
Form terkirim, tanpa error, tanpa hasil Nama field token salah
Token diterima, tetapi aksi tetap diblokir Sitekey tidak cocok dengan form yang dikirim
Percobaan pertama sukses, percobaan kedua gagal Token sekali pakai dipakai ulang
Lancar di browser, gagal di skrip Cookie atau header sesi tidak ikut terkirim

Siapkan tiga hal ini supaya diagnosis tidak berubah jadi tebak-tebakan:

  • Log dengan stempel waktu di dua titik: token diterima dan token dikirim.
  • Sitekey yang disalin langsung dari DOM form target, bukan dari dokumentasi.
  • Satu request percobaan yang bisa Anda ulang kapan saja dengan token baru.

Penyebab 1: token Turnstile sudah lewat masa berlaku

Token Turnstile berumur pendek — umumnya 300 detik, dan sebagian implementasi memangkasnya lebih pendek lagi. Yang menghabiskan jatah waktu itu biasanya bukan polling ke res.php, melainkan antrean di dalam aplikasi Anda sendiri.

Tiga pola yang menyisipkan jeda tanpa disadari:

  • Token disimpan ke Redis atau queue, lalu diambil worker lain beberapa menit kemudian.
  • Token dikumpulkan lebih dulu untuk satu batch penuh, baru dipakai satu per satu.
  • Coba ulang berjenjang yang menahan payload lama selama beberapa menit sebelum mengirimnya.

Perbaikannya: perlakukan token sebagai barang cepat basi. Selesaikan dan kirim di worker yang sama, tanpa mampir ke penyimpanan mana pun.

import requests
import time

API_KEY = "YOUR_API_KEY"

# Submit Turnstile task
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "turnstile",
    "sitekey": "0x4AAAAAAADnPIDROz1234",
    "pageurl": "https://staging.example.com/qa-login",
    "json": 1
}).json()

task_id = submit["request"]
time.sleep(10)

# Poll for result
for _ in range(24):
    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:
        token = result["request"]
        # USE TOKEN IMMEDIATELY — do not delay
        response = requests.post("https://staging.example.com/qa-login", data={
            "username": "user",
            "password": "pass",
            "cf-turnstile-response": token
        })
        break
    time.sleep(5)

Catatan untuk tim yang deploy dari Indonesia: dengan worker di AWS ap-southeast-1 (Singapura), ap-southeast-3 (Jakarta), atau GCP asia-southeast2, latensi jaringan bukan penyebab kedaluwarsa — 300 detik jauh lebih longgar. Yang memakan waktu adalah logika di antara polling dan submit.

Penyebab 2: token sekali pakai dikirim dua kali

Token Turnstile hanya sah untuk satu kali verifikasi. Blok coba ulang yang mengirim ulang payload dengan token lama selalu gagal di percobaan kedua, meski yang pertama sukses — dan gejalanya mirip masa berlaku habis.

Perbaikannya: satu percobaan submit, satu token baru. Pastikan fungsi penyelesaian ikut dipanggil ulang di dalam blok coba ulang, bukan hanya request-nya.

Pola khas: suite QA paralel yang gagal di skenario terakhir

Contohnya suite regression tim otomasi di Jakarta yang berjalan tiap malam: token Turnstile "kadang valid, kadang tidak", dan polanya hanya muncul saat suite dijalankan paralel. Setelah log diberi stempel waktu, penyebabnya kelihatan: worker mengumpulkan token untuk lima puluh skenario lebih dulu, baru menjalankan form satu per satu. Skenario di urutan akhir memakai token yang usianya sudah lewat lima menit.

Perbaikannya tidak menyentuh kode penyelesaian sama sekali — pemanggilan solve dipindahkan ke dalam masing-masing skenario. Pola paralel seperti ini terbantu model harga berbasis thread: paket ADVANCE ($90/bulan, 50 threads) memberi lima puluh penyelesaian bersamaan tanpa biaya per solve, jadi tidak ada alasan menumpuk token di awal demi berhemat. Untuk suite harian yang lebih kecil, STANDARD ($30/bulan, 15 threads) sudah memadai. Catatan pentingnya: thread menaikkan jumlah penyelesaian bersamaan, bukan memperpanjang masa berlaku token.

Penyebab 3: sitekey yang diselesaikan bukan milik form itu

Tiap widget Turnstile punya sitekey sendiri. Halaman yang memuat form login sekaligus form newsletter bisa punya dua sitekey berbeda, dan token dari widget pertama tidak akan diterima oleh endpoint yang memverifikasi widget kedua.

Jalankan ini di console browser pada halaman target:

// In browser console on the target page
document.querySelectorAll('[data-sitekey]').forEach(el => {
    console.log('Sitekey:', el.getAttribute('data-sitekey'));
    console.log('Element:', el);
});

Kalau hasilnya lebih dari satu, ambil sitekey yang berada di dalam form yang benar-benar Anda kirim — bukan yang pertama muncul di DOM.

Penyebab 4: nama field bukan cf-turnstile-response

Turnstile membaca token dari field cf-turnstile-response. Kesalahan klasiknya: menyalin integrasi reCAPTCHA lama dan lupa mengganti nama field — form tetap terkirim rapi, tanpa pesan error.

# WRONG — this is for reCAPTCHA
data = {"g-recaptcha-response": token}

# CORRECT — this is for Turnstile
data = {"cf-turnstile-response": token}

Sebagian backend memakai nama field kustom. Periksa dulu field apa yang benar-benar diisi widget di halaman target:

// Check what field the Turnstile widget populates
document.querySelector('[name*="turnstile"], [name*="cf-"]')

Cloudflare dapat memverifikasi token terhadap cookie sesi yang aktif saat widget dimuat. Kalau halaman dibuka dengan satu Session lalu token dikirim lewat requests.post yang berdiri sendiri, penolakan hampir pasti terjadi — dan tampilannya di log sama persis dengan token kedaluwarsa.

# Use the SAME session for page load and token submission
session = requests.Session()

# Load the page first to establish cookies
session.get("https://staging.example.com/qa-login")

# Then solve and submit using the same session
token = solve_turnstile(sitekey, pageurl)
session.post("https://staging.example.com/qa-login", data={
    "cf-turnstile-response": token
})

Satu objek Session untuk memuat halaman, menyelesaikan CAPTCHA, dan mengirim form. Ini sekaligus jawaban untuk keluhan "jalan di Postman, gagal di skrip Python": Postman menyimpan cookie dan header default yang tidak otomatis ditiru skrip Anda.

Penyebab 6: parameter action dan cData tidak ikut dikirim

Sebagian implementasi Turnstile mengikat token ke parameter action atau cData. Kalau situs mengharapkan keduanya tetapi Anda tidak menyertakannya saat mengirim task, token yang kembali secara teknis benar namun tidak cocok dengan yang diverifikasi server.

submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "turnstile",
    "sitekey": "0x4AAAAAAADnPIDROz1234",
    "pageurl": "https://staging.example.com/qa-login",
    "action": "login",           # If required by the site
    "data": "custom_cdata_value", # If required by the site
    "json": 1
}).json()

Kedua nilainya terbaca di atribut data-action dan data-cdata pada elemen widget.

Alur diagnosis: dari token ditolak sampai akar masalah

Ikuti urutan ini dari atas dan berhenti di cabang pertama yang jawabannya "tidak". Masa berlaku ditaruh paling depan karena paling murah dibuktikan: cukup satu solve ulang.

Token solved but rejected
    ↓
Used within 5 minutes? → No → Solve again, submit immediately
    ↓ Yes
Correct sitekey? → No → Find the correct sitekey from the page
    ↓ Yes
Using cf-turnstile-response field? → No → Change field name
    ↓ Yes  
Same session for page load + submit? → No → Use session persistence
    ↓ Yes
Token used only once? → No → Solve a new token per submission
    ↓ Yes
Site requires action/cData? → Check page source, add to API call

Pertanyaan yang sering muncul

Bagaimana membedakan token kedaluwarsa dari sitekey yang salah?

Kirim ulang task, lalu submit tokennya dalam waktu kurang dari sepuluh detik. Kalau lolos, penyebabnya masa berlaku. Kalau tetap 403 dengan token yang masih segar, lanjut ke pemeriksaan sitekey dan nama field.

Apakah satu token bisa dipakai untuk dua kali submit?

Tidak. Token bersifat sekali pakai, jadi setiap percobaan — termasuk coba ulang setelah batas waktu — memerlukan token baru dari task yang baru.

Apakah menambah thread membuat token bertahan lebih lama?

Tidak. Thread menentukan berapa banyak penyelesaian yang berjalan bersamaan, misalnya 50 threads di paket ADVANCE ($90/bulan) atau 15 threads di STANDARD ($30/bulan). Masa berlaku token ditentukan Cloudflare, bukan paket Anda.

Kapan harus memakai method cloudflare_challenge, bukan turnstile?

Pakai turnstile untuk widget yang tertanam di dalam halaman, dan cloudflare_challenge untuk halaman interstisial penuh yang muncul sebelum konten tampil. Method yang keliru menghasilkan token yang tidak akan pernah cocok.

Apakah alur ini masih berlaku kalau situs pindah ke hCaptcha?

Tidak. hCaptcha dan FunCaptcha belum didukung, dan GeeTest v4 masih berstatus segera hadir. Yang tersedia mencakup reCAPTCHA v2 dan v3, Turnstile, Cloudflare Challenge, GeeTest v3, serta CAPTCHA gambar dan grid, ditambah CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta).


Uji token Turnstile Anda sekarang

Ambil API key di captchaai.com, jalankan satu task Turnstile ke halaman staging Anda, lalu cocokkan hasilnya dengan alur diagnosis di atas. Kalau masih tersangkut, empat panduan berikut membahas tiap cabangnya:

Komentar dinonaktifkan untuk artikel ini.