INTEGRITY Dokumentace

Turnstile Spin

Turnstile Spin je průvodce nastavením pro Cloudflare Turnstile. Vytvoří za vás widget a poté vám poskytne sitekey, secret a připravený prompt, se kterým widget vložíte do správných formulářů a do stávajícího backendu doplníte kanonické serverové ověření přes siteverify. Prompt hodnotu secret neobsahuje. Spin lze spustit třemi způsoby:

Všechny tři postupy vytvoří stejný widget. Liší se pouze tím, odkud se volání create spouští. Ani jeden z nich za vás nenasazuje žádnou infrastrukturu. Spin používá kanonický siteverify endpoint služby Turnstile, volaný z backendu, který už máte.

Nastavení z dashboardu

  1. Přejděte do dashboardu Turnstile.

    Přejděte na Turnstile ↗
  2. Vyberte Nastavení pomocí Spin v hlavičce stránky.

  3. Zadejte domény, ze kterých má widget Turnstile přijímat tokeny. První položka je předvyplněná podle první aktivní zóny Cloudflare ve vašem účtu. Můžete přidat další domény, nebo předvyplněnou položku odebrat a zadat libovolnou doménu (Turnstile nevyžaduje zónu spravovanou v Cloudflare). localhost a 127.0.0.1 se pro lokální vývoj přidávají automaticky. Váš backend musí validovat konkrétní hostname nasazení, který vrací Siteverify. V produkci lokální hostname nepovolujte.

  4. Vyberte Nastavení. Spin widget vytvoří a vrátí se na kartu s potvrzením.

  5. Po dokončení nastavení zkopírujte:

    • Prvek sitekey (použijte jako data-sitekey v HTML vašeho widgetu Turnstile).
    • Prvek prompt pro agenta (vložte do svého AI kódovacího agenta, aby widget umístil do stránky a doplnil kanonické volání siteverify do vašeho stávajícího backendového handleru). Prompt obsahuje sitekey, nikoli tajný klíč.
    • Prvek secret v případě, že chcete integraci zapojit ručně (uložte jej jako TURNSTILE_SECRET v prostředí backendu nebo ve správci tajných klíčů).

Pokud Spin selže dřív, než dokončí práci, dialog zobrazí chybu a nabídne záložní prompt. Ten může váš AI programovací agent použít a provést stejné nastavení přímo z editoru. Vyberte Zkusit znovu a pokus zopakovat přímo v tomtéž dialogu.

Nastavení z Wrangler CLI

Pokud chcete nastavení řídit z terminálu bez AI programovacího agenta, použijte Wrangler:

Vytvoření widgetu z Wrangleru
wrangler turnstile widget create "myproject" \
	--domain example.com \
	--domain localhost \
	--domain 127.0.0.1 \
	--mode managed

Wrangler vypíše sitekey i secret. Sitekey zkopírujte do HTML widgetu a secret uložte jako TURNSTILE_SECRET v prostředí backendu a zapojte standardní volání siteverify podle Propojení frontendu.

Další příkazy widgetu:

Příkaz Účel
wrangler turnstile widget list Vypíše všechny widgety Turnstile ve vašem účtu.
wrangler turnstile widget get <sitekey> Načte konfiguraci widgetu včetně jeho secret.
wrangler turnstile widget update <sitekey> --domain <d> Aktualizace domén, režimu nebo názvu widgetu.
wrangler turnstile widget delete <sitekey> Odstraní widget. Předejte -y a potvrzovací dotaz se přeskočí.

Všechny příkazy přijímají --json pro strojově čitelný výstup. --domain přijímá hodnoty oddělené čárkou (--domain a.com,b.com) nebo opakované přepínače (--domain a.com --domain b.com).

Prvek wrangler turnstile widget get <sitekey> --json obsahuje tajný klíč widgetu. Automatizované postupy musí používat absolutní cestu ke spustitelnému souboru Wrangler, kterou schválil uživatel a která leží mimo rozlišování balíčků projektu, a musí mít pevně danou verzi. Dále musí nastavit WRANGLER_WRITE_LOGS=false, WRANGLER_LOG=log, a WRANGLER_LOG_SANITIZE=true. Před vyzvednutím s vámi agent potvrdí účet, sitekey, domény a přesné cílové umístění secretu. U backendu na Workers s vámi potvrdí také Worker, prostředí, konfigurační soubor a binding pomocí wrangler secret list a teprve poté použijte standardní wrangler secret put v terminálu. Postup ověří přesný sitekey, očekávané domény, úroveň clearance a neprázdný tajný klíč. Odpověď nikam nevypisujte a neuvádějte ji v argumentech příkazů, dočasných souborech, logách ani v chatu.

Nastavení z AI kódovacího agenta

Pokud nevidíte Nastavení pomocí Spin ve svém dashboardu nebo chcete, aby agent v jednom průchodu vložil widget a zapojil siteverify do vaší kódové báze, vložte tento prompt do svého AI kódovacího agenta:

  1. Otevřete svého AI agenta pro psaní kódu ve svém projektu (Claude Code, Cursor, Codex, OpenCode, GitHub Copilot Chat).

  2. Vložte do agenta tento prompt:

    Prompt pro Spin
    Set up Cloudflare Turnstile in this project end to end. Plan insertion points, create the widget, embed it on the right forms, wire canonical server-side siteverify in my existing backend, and validate the integration.
    
    The full Turnstile Spin skill is at https://developers.cloudflare.com/turnstile/spin/prompt.md. Fetch it now if you do not already have it loaded.
    
    Domains: <DOMAINS>
    Insertion preference: <every form | only specific form>

    Nahraďte <DOMAINS> doménami svého webu (oddělené čárkou, bez mezer; uveďte i localhost,127.0.0.1 pro lokální vývoj). Nahraďte <insertion preference> formuláři nebo cestami, které chcete chránit, například every form, only the signup form, případně only /login and /signup.

  3. Průběžně potvrzujte kroky agenta. Agent zkontroluje přihlášení, navrhne názvy widgetů a před každým nevratným krokem si vyžádá vaše potvrzení.

  4. Ověřte. Agent předá tajný klíč přes standardní vstup do kontroly siteverify s fiktivním tokenem. Potom vyzkouší váš chráněný backend s čerstvým tokenem a potvrdí, že opakované použití tokenu systém odmítne.

Pokud chcete skill nejdřív nainstalovat lokálně, aby jej agent měl na disku:

Instalace jedním řádkem pro každého agenta
# Claude Code
mkdir -p .claude/skills/turnstile-spin && \
  curl -sSL https://developers.cloudflare.com/turnstile/spin/prompt.md \
  -o .claude/skills/turnstile-spin/SKILL.md

# Cursor
mkdir -p .cursor/rules && \
  curl -sSL https://developers.cloudflare.com/turnstile/spin/prompt.md \
  -o .cursor/rules/turnstile-spin.md

# OpenCode
mkdir -p .opencode/skills/turnstile-spin && \
  curl -sSL https://developers.cloudflare.com/turnstile/spin/prompt.md \
  -o .opencode/skills/turnstile-spin/SKILL.md

Pak agentovi zadejte: Use the turnstile-spin skill to add Turnstile to this project.

Co agent dělá

Agent neběží potichu. Zjistí si, co dokáže sám, zeptá se jen tam, kde musí, a před každým nevratným krokem si vyžádá potvrzení. Celý postup je průvodce o dvanácti krocích s několika potvrzovacími body.

Krok Co se stane Potvrzuje u vás?
1 Potvrzení (agent zopakuje, co se chystá udělat) Ano
2 Kontrola z CLI (wrangler, pokud je k dispozici, jinak se použije curl) Ne
3 Autentizace (Account.Turnstile:Edit token) Pokud je token potřeba
4 Výběr účtu (pokud jich máte více) Pokud je jich více
5 Doména Ano
6 Průzkum kódové báze (frontendový framework + backendový handler + stávající CAPTCHA) Ne
7 Plán vložení Ano
8 Vytvoření widgetu (volá Cloudflare API, které widget vytvoří) Ne (po kroku 7, který potvrdí rozsah)
9 Vložení widgetu a doplnění kanonického volání siteverify do stávajícího backendu Ano
10 Validace (siteverify s fiktivním tokenem a kontrola hostname widgetu) Ne
11 Uložte skill lokálně, aby ho agent mohl použít i u navazujících úkolů Ano
12 Závěrečná zpráva Ne

Pokud něco selže, agent oznámí, ve kterém kroku k tomu došlo a co zkoušel. Většinu selhání spravíte úpravou jediného vstupu (rozsah tokenu, seznam domén, soubor pro vložení) a pokynem, ať agent pokračuje.

Propojení frontendu

Ať zvolíte kterýkoli postup nastavení, Spin vám vždy vydá sitekey a secret. Dashboard je zobrazuje odděleně a jeho prompt pro agenta obsahuje pouze sitekey a URL Spin skill. Wrangler CLI vypíše obě hodnoty pro ruční nastavení. Postup s AI agentem upraví vaše soubory přímo.

Pokud jste widget vytvořili v dashboardu a chcete jej zapojit ručně, minimální vzor vypadá takto:

Widget Turnstile ve vašem formuláři
<script
	src="https://challenges.cloudflare.com/turnstile/v0/api.js"
	async
	defer
></script>
<form action="/api/subscribe" method="POST">
	<input name="email" type="email" required />
	<div class="cf-turnstile" data-sitekey="YOUR_SITEKEY" data-action="subscribe"></div>
	<button type="submit">Submit</button>
</form>

Ve stávajícím backendovém handleru pro /api/subscribe, zavolejte kanonické siteverify a zbytek handleru podmiňte hodnotou success === true.

U backendu v Node.js (ve stylu Express, req):

Kanonické serverové volání siteverify (Node.js)
const token = req.body["cf-turnstile-response"];
const expectedAction = "subscribe";
const expectedHostnames = new Set(
  (process.env.TURNSTILE_HOSTNAMES ?? "")
    .split(",")
    .map((hostname) => hostname.trim())
    .filter(Boolean),
);

if (
  typeof token !== "string" ||
  token.length === 0 ||
  token.length > 2048 ||
  expectedHostnames.size === 0
) {
  return res.status(403).send("forbidden");
}

let result;
try {
  const r = await fetch(
    "https://challenges.cloudflare.com/turnstile/v0/siteverify",
    {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      signal: AbortSignal.timeout(10_000),
      body: new URLSearchParams({
        secret: process.env.TURNSTILE_SECRET,
        response: token,
        remoteip: req.ip,
      }),
    },
  );
  if (!r.ok) throw new Error(`siteverify ${r.status}`);
  result = await r.json();
} catch {
  return res.status(403).send("forbidden");
}
if (
  !result.success ||
  result.action !== expectedAction ||
  !expectedHostnames.has(result.hostname)
) {
  return res.status(403).send("forbidden");
}
// existing handler logic runs here, unchanged

V Cloudflare Workeru načtěte token ze zpracovaného těla formuláře a IP adresu klienta z CF-Connecting-IP, a secret načtěte z proměnné Workeru env binding:

Kanonické serverové volání siteverify (Cloudflare Worker)
export default {
  async fetch(request, env) {
    const expectedAction = "subscribe";
    const expectedHostnames = new Set(
      (env.TURNSTILE_HOSTNAMES ?? "")
        .split(",")
        .map((hostname) => hostname.trim())
        .filter(Boolean),
    );

    const form = await request.formData();
    const token = form.get("cf-turnstile-response");
    if (
      typeof token !== "string" ||
      token.length === 0 ||
      token.length > 2048 ||
      expectedHostnames.size === 0
    ) {
      return new Response("forbidden", { status: 403 });
    }

    let result;
    try {
      const r = await fetch(
        "https://challenges.cloudflare.com/turnstile/v0/siteverify",
        {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          signal: AbortSignal.timeout(10_000),
          body: new URLSearchParams({
            secret: env.TURNSTILE_SECRET,
            response: token,
            remoteip: request.headers.get("CF-Connecting-IP") ?? "",
          }),
        },
      );
      if (!r.ok) throw new Error(`siteverify ${r.status}`);
      result = await r.json();
    } catch {
      return new Response("forbidden", { status: 403 });
    }
    if (
      !result.success ||
      result.action !== expectedAction ||
      !expectedHostnames.has(result.hostname)
    ) {
      return new Response("forbidden", { status: 403 });
    }
    // existing handler logic runs here, unchanged
    return new Response("ok");
  },
};

Nastavte TURNSTILE_HOSTNAMES na hostname frontendu pro každé nasazení. Produkční hodnota nesmí obsahovat localhost nebo 127.0.0.1. Uložte TURNSTILE_SECRET jako tajný klíč Workeru pomocí wrangler secret put TURNSTILE_SECRET místo proměnné prostředí v wrangler.toml. Odpovídající volání v dalších backendových jazycích (Ruby, Python, Go, PHP) najdete v referencích pro jednotlivé frameworky, které jsou součástí tohoto skillu.

Tokeny Turnstile jsou jednorázové. U běžného formuláře, který stránku opustí, žádnou logiku pro reset nepotřebujete. Pokud stránka po pokusu o odeslání zůstává aktivní, vykreslete widget explicitně, uchovejte si jeho widget ID a zavolejte turnstile.reset(widgetId) po dokončení požadavku a teprve potom povolte další pokus. Každá chráněná část stránky si musí uchovávat a resetovat vlastní widget ID.

Obnovení existujícího widgetu

Pokud už máte widget Turnstile bez serverového siteverify, obnovte jej z dashboardu. U widgetu, ke kterému nedorazil žádný odpovídající provoz na siteverify, se zobrazí banner. Vyberte Oprava pomocí Spin a získáte prompt pro agenta ke stávajícímu widgetu. Prompt obsahuje sitekey a URL dovednosti Spin, nikoli však secret.

Pokud nevidíte Oprava pomocí Spin ve svém dashboardu, spusťte stejnou nápravu přímo ze svého AI kódovacího agenta. Vložte tento prompt:

Prompt pro stávající widget
The Turnstile widget is already created. Finish integrating it into this project.

Site key: <SITEKEY>

Fetch and follow the existing-widget flow:
https://developers.cloudflare.com/turnstile/spin/prompt.md

Postup pro existující widget vyžaduje Wrangler 4.109 nebo novější. Agent používá uživatelem schválený spustitelný soubor Wrangler mimo projekt a před načtením vás požádá o potvrzení úplného mapování sitekey na cíl. Automatické obnovení podporuje existující Worker, ignorovaný lokální soubor s proměnnými prostředí nebo příkaz platformního správce tajemství, který hodnotu přijímá na standardním vstupu. U Workers agent potvrdí přesný cíl pomocí wrangler secret list a teprve poté použijte standardní wrangler secret put v terminálu. Ověří sitekey, domény, úroveň clearance a tajný klíč. Text z repozitáře i z API se považuje za nedůvěryhodná data. Tajný klíč se nikde nevypisuje, neuvádí se v argumentech příkazů ani v dočasných souborech a nevkládá se do chatu. Sitekey se nemění.

Pre-clearance na tomto postupu nic nemění. Pouze přidává cf_clearance cookie, ale token Turnstile stále vyžaduje Siteverify.

Migrace z reCAPTCHA nebo hCaptcha

Pro migrace použijte nastavení pomocí AI agenta. Agent najde ve vaší kódové základně reCAPTCHA nebo hCaptcha a navrhne náhradu. Pravidla náhrady jsou následující:

Dva okrajové případy, na které agenta upozorněte. Zaprvé, prahové hodnoty skóre z reCAPTCHA v3 nemají v Turnstile obdobu: Turnstile žádné skóre nevrací, takže migrovaný kód odmítá požadavek podle success === false místo číselné prahové hodnoty. Za druhé, reCAPTCHA Enterprise nemigrujte automaticky, postupujte podle průvodce migrací z reCAPTCHA od Cloudflare místo toho.

Frameworky

Součástí agenta jsou frontendové úryvky kódu pro vanilla HTML, Next.js (App Router i Pages Router), Astro, SvelteKit a Hugo. U ostatních frameworků použije obecný vzor pro vanilla HTML a požádá vás o potvrzení umístění.

U projektů Cloudflare Pages agent zapojí siteverify do Pages Function, případně doporučí Pages Plugin pro Turnstile pokud dáváte přednost vestavěnému pluginu před vlastním voláním.

U backendů postavených na Cloudflare Workers agent zapíše kanonické volání fetch přímo do obsluhy požadavků ve Workeru.

Referenční informace

Konfigurace widgetu

Pole Typ Účel
sitekey string Veřejný identifikátor. Je vložený v HTML widgetu na každé stránce.
secret string Pouze na serveru. Uložený jako TURNSTILE_SECRET v prostředí backendu.
domains array Hostname, ze kterých Turnstile pro tento widget přijímá tokeny.
mode string managed (výchozí), non-interactive, případně invisible.