Explainers

Modos de widget Cloudflare Turnstile: gerenciado, não interativo, invisível

Um mesmo site pode mostrar o Cloudflare Turnstile de três formas diferentes — às vezes aparece uma caixa de seleção, às vezes só um spinner passa rápido pela tela, às vezes não há nada visível. Isso não é inconsistência do site: é o modo do widget, e cada um exige uma estratégia de detecção própria antes da resolução automática.

  • Gerenciado: a Cloudflare decide o nível de desafio por visitante; às vezes aparece um checkbox.
  • Não interativo: só prova de trabalho em segundo plano; nunca mostra interface, falha em vez de escalar.
  • Invisível: nenhum contêiner visível; roda no carregamento da página ou por gatilho de código.

Para automação, os três produzem o mesmo resultado — um token cf-turnstile-response — resolvido com o mesmo método turnstile na API da CaptchaAI.


Modo gerenciado: o padrão do Cloudflare Turnstile

No modo gerenciado, a própria Cloudflare decide o nível de desafio para cada visitante, com base na reputação do tráfego. No HTML, o widget é declarado assim:

<!-- Managed mode (default) -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-theme="light">
</div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

O que muda para quem automatiza

O modo gerenciado reage aos sinais do navegador que faz a requisição:

  • Alta confiança: o widget passa invisível, sem nenhuma UI visível.
  • Confiança média: aparece um checkbox — clique para verificar.
  • Baixa confiança: desafio interativo mais complexo, ou bloqueio direto.

Esse é o modo mais comum — e também o mais imprevisível para automação, já que o mesmo site pode ou não mostrar UI dependendo dos sinais do momento.

Como detectar no HTML

def is_managed_mode(html):
    """Check if Turnstile is using managed mode (default)."""
    # Managed mode is the default — no explicit mode attribute
    has_turnstile = "cf-turnstile" in html
    has_explicit_mode = 'data-appearance="interaction-only"' in html or \
                        'data-appearance="always"' in html or \
                        'appearance: "interaction-only"' in html
    return has_turnstile and not has_explicit_mode

Dica: registre o valor de data-appearance mesmo quando ausente — a ausência do atributo já é o sinal de que o site está em modo gerenciado.


Modo não interativo do Turnstile: sem clique, só spinner

O modo não interativo nunca mostra checkbox nem qualquer elemento clicável. Ele roda um desafio de prova de trabalho em segundo plano e exibe apenas um spinner de carregamento. Se o desafio não fechar de forma não interativa, ele falha — não existe escalonamento para checkbox. No HTML:

<!-- Non-interactive mode -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-appearance="interaction-only">
</div>

Ou via API JavaScript:

turnstile.render('#turnstile-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    appearance: 'interaction-only',
    callback: function(token) {
        document.getElementById('cf-turnstile-response').value = token;
    },
});

Como funciona o fluxo

Page loads → Widget initializes
    ↓
Background proof-of-work runs
    ↓
Success → Token generated (no visible UI)
    OR
Failure → Widget reports error (no fallback to checkbox)

Onde esse modo aparece na prática

Aparece em formulários de comentário, inscrição em newsletter, ações de baixo risco onde qualquer atrito atrapalha a conversão, e endpoints de API com proteção no navegador.


Modo invisível do Turnstile: nenhum elemento aparece na tela

O modo invisível é invisível de verdade — nenhum contêiner ocupa espaço na viewport. O widget carrega junto com a página (ou é disparado via código) e entrega o token sem qualquer sinal visual. Declaração no HTML:

<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
     class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-size="invisible">
</div>

Ou totalmente via JavaScript:

// Programmatic invisible Turnstile
turnstile.render('#hidden-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    size: 'invisible',
    callback: function(token) {
        // Token ready — submit form automatically
        submitForm(token);
    },
    'error-callback': function() {
        // Challenge failed
        console.error('Invisible Turnstile failed');
    },
});

Por que é mais difícil de detectar

O Turnstile invisível é mais difícil de identificar porque o contêiner não tem dimensão nenhuma no HTML renderizado:

import re

def detect_invisible_turnstile(html):
    """Detect invisible Turnstile on a page."""
    indicators = {
        "script_loaded": "challenges.cloudflare.com/turnstile" in html,
        "size_invisible": 'data-size="invisible"' in html or
                          "size: 'invisible'" in html or
                          'size: "invisible"' in html,
        "api_render_call": "turnstile.render" in html,
        "response_field": "cf-turnstile-response" in html,
    }

    if indicators["script_loaded"] and indicators["size_invisible"]:
        return {"mode": "invisible", "confidence": "high"}
    elif indicators["script_loaded"] and indicators["api_render_call"]:
        return {"mode": "invisible_or_programmatic", "confidence": "medium"}
    elif indicators["response_field"]:
        return {"mode": "turnstile_present", "confidence": "low"}

    return {"mode": "none", "confidence": "high"}

Times de QA no Brasil costumam rodar esse tipo de verificação em staging hospedado na região sa-east-1 (São Paulo) da AWS — isso isola a latência do desafio de prova de trabalho da latência de rede até o servidor de origem.

Dica: como o modo invisível costuma servir para verificação em segundo plano, trate tokens e metadados do desafio como qualquer outro dado de sessão — revise as obrigações da LGPD antes de registrar isso em log.


Como extrair a sitekey em qualquer modo do Turnstile

Não importa o modo — a sitekey é sempre necessária para enviar a tarefa à CaptchaAI. Esta função tenta os três padrões mais comuns de onde ela aparece:

import re

def extract_turnstile_sitekey(html):
    """Extract Turnstile sitekey from page HTML (works for all modes)."""

    # Pattern 1: data-sitekey attribute in HTML
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html)
    if match:
        return match.group(1)

    # Pattern 2: JavaScript render call
    match = re.search(r"sitekey:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    # Pattern 3: Turnstile config object
    match = re.search(r"siteKey['\"]?\s*[:=]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    return None

Resolvendo os três modos do Turnstile com a mesma chamada de API

Gerenciado, não interativo ou invisível — a CaptchaAI resolve os três exatamente da mesma forma. O modo do widget não muda nada na chamada à API:

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_turnstile(sitekey, page_url):
    """Solve any Turnstile mode — managed, non-interactive, or invisible."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    })

    task_id = submit.json()["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("Turnstile solve timed out")


# Use with any mode
token = solve_turnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
print(f"Token: {token[:50]}...")

Node.js

const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveTurnstile(sitekey, pageUrl) {
  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method: "turnstile",
      sitekey,
      pageurl: pageUrl,
      json: 1,
    },
  });

  const taskId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId, json: 1 },
    });

    if (result.data.status === 1) {
      return result.data.request;
    }
  }

  throw new Error("Turnstile solve timed out");
}

// Same function works for all Turnstile modes
solveTurnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://staging.example.com/qa-login")
  .then((token) => console.log("Token:", token.substring(0, 50)));

Atenção: o loop de 60 tentativas cobre até 5 minutos — bem mais do que o Turnstile costuma levar. Ajuste esse limite conforme o SLA do seu pipeline.


Problemas comuns no Cloudflare Turnstile e como resolver

  • Token válido, mas o formulário rejeita: a sitekey usada é diferente da que o widget realmente renderizou — confira o valor gerado via JavaScript, não só o HTML estático.
  • Widget não aparece no HTML: o modo invisível carregou depois da renderização inicial — espere o carregamento completo da página e confira as respostas XHR.
  • Vários widgets de Turnstile na mesma página: sitekeys diferentes para formulários diferentes — associe cada sitekey ao formulário certo.
  • data-size="compact" confunde a detecção: compact é uma variante de tamanho, não um modo — por padrão usa o modo gerenciado.
  • Atributo data-action presente: é uma tag para análise, não indica o modo — inclua a action na resolução só se a validação exigir.
  • Token expira antes do envio: tokens do Turnstile expiram em 300 segundos — resolva o desafio o mais perto possível do envio.

Perguntas frequentes

O modo do Turnstile muda como a CaptchaAI resolve o desafio?

Não. A CaptchaAI usa o mesmo método turnstile para os três modos — gerenciado, não interativo e invisível. Sitekey e URL da página são os únicos parâmetros exigidos.

Como eu descubro qual modo um site está usando, só olhando o HTML?

Procure pelos atributos data-appearance e data-size. data-size="invisible" indica modo invisível; data-appearance="interaction-only" indica não interativo. Se nenhum aparecer, é o modo gerenciado — o padrão da Cloudflare.

O modo invisível é mais difícil de automatizar do que o gerenciado?

Para detecção, sim — o contêiner não tem dimensão visível, então é preciso inspecionar o script carregado e as chamadas de turnstile.render para achar a sitekey. Para a resolução em si, não: a chamada à API é idêntica nos três modos.

Por quanto tempo um token do Turnstile continua válido depois de gerado?

300 segundos. Se o formulário não for enviado dentro desse intervalo, o token expira e a resolução precisa ser refeita — por isso o ideal é resolver o desafio o mais perto possível do envio.

Cloudflare Turnstile e Cloudflare Challenge são a mesma coisa?

Não. O Turnstile é o widget "sem cliques" que sites de terceiros incorporam na própria página; o Cloudflare Challenge é a página de verificação que a Cloudflare exibe na frente de um site inteiro. A CaptchaAI resolve os dois, mas com métodos de API diferentes — turnstile e cloudflare_challenge.


Em resumo

Os três modos do Cloudflare Turnstile mudam o que o usuário vê, mas entregam o mesmo token cf-turnstile-response, resolvido pelo solucionador de Cloudflare Turnstile da CaptchaAI com o método turnstile e alta taxa de sucesso nos três casos. A diferença para quem desenvolve está na detecção: o gerenciado deixa HTML visível para inspecionar; o invisível exige análise mais profunda da página até achar a sitekey. Consulta rápida:

Característica Gerenciado Não interativo Invisível
Widget aparece na tela? Às vezes Nunca (só o spinner) Nunca
Precisa de elemento contêiner? Sim Sim Sim (oculto)
Exige interação do usuário? Às vezes (checkbox) Não Não
Roda desafio de prova de trabalho? Sim (pode escalar) Sim (sempre) Sim (sempre)
Tem fallback para checkbox interativo? Sim Não (falha em vez disso) Não (falha em vez disso)
Onde costuma aparecer Login, cadastro Formulários de baixo atrito Verificação em segundo plano

Artigos relacionados

Os comentários estão desativados para este artigo.