Coloque um captcha em qualquer formulário
O Qlyne Verify barra robôs nos seus formulários sem quebra-cabeça e sem o conector. O navegador resolve um proof-of-work curto (cerca de um segundo num celular), e o formulário leva um token que o seu servidor confere com o Qlyne antes de aceitar. Funciona em qualquer hospedagem: Vercel, Netlify, servidor próprio.
1. Ponha a verificação no formulário
Seção intitulada “1. Ponha a verificação no formulário”O identificador do site está no menu do site no painel, e lá também estão estas duas linhas já preenchidas (Bots, “Captcha for any form”):
<script src="https://wait.qlyne.com/src/verify.js" async></script>
<form method="post" action="/contato"> <!-- seus campos --> <div class="qlyne-verify" data-site="id_do_seu_site" data-core-url="https://api.qlyne.com"></div> <button>Enviar</button></form>A caixa mostra “Conferindo o navegador…” e depois “Navegador conferido”. O token vai num campo escondido chamado qlyne-token, dentro do formulário.
Opções no div:
| Atributo | O que faz |
|---|---|
data-theme | light ou dark; por padrão segue o sistema do visitante |
data-field | outro nome para o campo escondido |
data-callback | nome de uma função global que recebe o token |
A página também recebe o evento qlyne:verified, com o token em event.detail.token.
Em Bots, “Challenge”, a opção “Click to confirm” faz a caixa pedir um clique em “Sou uma pessoa”: sempre, ou só quando o visitante parece robô (o widget manda as mesmas contagens da nota de robô, nunca teclas ou texto). O token vem depois do clique.
Cadastre o endereço do seu site em Site settings, “Allowed origins”, para só as suas páginas pedirem desafios. Vazio libera qualquer site.
2. Confira o token no servidor
Seção intitulada “2. Confira o token no servidor”Antes de aceitar o formulário, mande o token ao Qlyne com a chave de API da conta (Plan and API key). Guarde a chave no servidor: quem a lê no navegador confere tokens em seu nome.
curl -X POST "https://api.qlyne.com/verify/check?tenant_id=id_do_seu_site" \ -H "Authorization: Bearer $QLYNE_API_KEY" -H "Content-Type: application/json" \ -d '{"token": "valor de qlyne-token", "remoteip": "IP do visitante"}'Respostas:
| Resposta | Significado |
|---|---|
{"success": true, "solved_at": 1791300000, "origin": "https://loja.exemplo"} | Aceite o formulário |
{"success": false, "error": "invalid_or_expired"} | Ausente, errado, já usado ou com mais de 5 minutos |
{"success": false, "error": "ip_mismatch"} | Resolvido de outro IP que não o de remoteip |
| HTTP 401 | Chave de API errada |
O remoteip é opcional. Mande quando o seu servidor souber o IP real do visitante; atrás de um proxy, use o endereço que o proxy repassa.
Node.js (route handler do Next.js)
Seção intitulada “Node.js (route handler do Next.js)”export async function POST(request) { const form = await request.formData(); const res = await fetch(`${process.env.QLYNE_CORE_URL}/verify/check?tenant_id=${process.env.QLYNE_SITE}`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.QLYNE_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ token: form.get('qlyne-token') }), }); const check = await res.json(); if (!check.success) return Response.json({ error: 'Tente de novo.' }, { status: 400 }); // ...trate o formulário return Response.json({ ok: true });}PHP (Laravel)
Seção intitulada “PHP (Laravel)”$check = Http::withToken(env('QLYNE_API_KEY')) ->post(env('QLYNE_CORE_URL').'/verify/check?tenant_id='.env('QLYNE_SITE'), [ 'token' => $request->input('qlyne-token'), 'remoteip' => $request->ip(), ])->json();abort_unless($check['success'] ?? false, 422, 'Tente de novo.');import os, requests
check = requests.post( f"{os.environ['QLYNE_CORE_URL']}/verify/check", params={"tenant_id": os.environ["QLYNE_SITE"]}, headers={"Authorization": f"Bearer {os.environ['QLYNE_API_KEY']}"}, json={"token": form["qlyne-token"]}, timeout=5,).json()if not check.get("success"): raise ValueError("captcha")Formulário enviado por JavaScript
Seção intitulada “Formulário enviado por JavaScript”O token vale uma vez. Se a página envia o formulário com fetch e continua aberta, peça um token novo depois de cada envio:
QlyneVerify.reset(document.querySelector('.qlyne-verify'));O widget também renova o token sozinho antes de vencer, então um formulário aberto há um tempo continua valendo.
Sites com Content Security Policy
Seção intitulada “Sites com Content Security Policy”Se o seu site manda o cabeçalho Content-Security-Policy, libere o script e as chamadas ao Qlyne:
script-src 'self' https://wait.qlyne.com; connect-src 'self' https://api.qlyne.comO widget não precisa de mais nada: o estilo dele não conta como estilo embutido, e se a política não deixa criar worker a partir de blob:, ele resolve o desafio na própria página. Acrescentar worker-src blob: deixa ele resolver em segundo plano, o que mantém um celular lento respondendo.
O que é contado
Seção intitulada “O que é contado”Em Bots, no painel: captchas resolvidos, navegadores automatizados recusados (o widget percebe navegador comandado por Selenium, Puppeteer ou Playwright) e tokens que o seu servidor recusou. Nenhum dado pessoal fica guardado: o token leva a hora, a origem e o IP do visitante (o /64 no IPv6) por 5 minutos, só para comparar com o remoteip.