> ## Documentation Index
> Fetch the complete documentation index at: https://docs.botshield.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare Turnstile

> Optional. Put Cloudflare's free bot filter in front of the BotShield widget so automated traffic can't hammer the human check, and receive the Turnstile token with every result.

Cloudflare Turnstile and BotShield Gate answer different questions, one under the other. Turnstile asks *does this traffic look automated?* and costs the visitor nothing. BotShield Gate asks *is a real human here, right now?* and answers with a signed fact. Putting Turnstile in front keeps cheap automation away from the button, so the human check is spent where a browser check can't tell: on traffic that looks human. Real people pass. Automation that gets past Turnstile doesn't, because it can't produce a live confirmation on a phone.

You do not need Turnstile to use BotShield Gate. If you already run it, or you want a free first filter on a high-traffic form, connect it once in the Console and the widget does the rest.

<Note>
  Turnstile never changes a gate's result. **Verified** and **Unavailable** come from the human check alone; BotShield does not score, and a Turnstile failure is never reported as "a bot." See [Result states](/concepts/result-states).
</Note>

## What connecting it does

1. **The widget loads Turnstile for you.** On any page that has `<botshield-verify>`, the widget renders an invisible Turnstile challenge with your site key. No second script tag, no visible widget.
2. **You receive the token with the result.** The `botshield:success` event carries `turnstile_token` alongside `token` and `request_id`. Validate it with Cloudflare `siteverify` on your server, and decline the submission if it fails.
3. **Fail closed.** Require both: a valid Turnstile token and a verified BotShield result. A missing or failed Turnstile token means the request did not come through the widget on a real page.

## Connect it in the Console

<Steps>
  <Step title="Get your Turnstile keys">
    In the Cloudflare Dashboard, open **Turnstile** and create a widget for your site. Copy the **site key** and the **secret key**.
  </Step>

  <Step title="Open the Cloudflare card">
    In the BotShield Console, go to **Integrations** and select **Configure** on the **Cloudflare** card.
  </Step>

  <Step title="Save the keys">
    Switch the integration on, paste the **Site Key** and **Secret Key**, and select **Save**. The card shows **Connected**.
  </Step>
</Steps>

The widget picks the change up on its next load. If you also render your own Turnstile widget on the same page, remove it: the one the BotShield widget renders is enough, and two widgets on one page produce two tokens.

## Validate the token on your server

```typescript theme={null}
const TURNSTILE_VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify";

async function turnstilePassed(token: string, secret: string, ip?: string): Promise<boolean> {
  if (!token) return false;
  const body = new URLSearchParams({ secret, response: token });
  if (ip) body.set("remoteip", ip);
  const res = await fetch(TURNSTILE_VERIFY_URL, { method: "POST", body });
  if (!res.ok) return false;
  const result = (await res.json()) as { success: boolean };
  return result.success === true;
}
```

On the page, read the token from the success event and send it with the BotShield result:

```html theme={null}
<botshield-verify site-key="pk_live_…" scope="signup" scan-mode="modal"></botshield-verify>

<script>
  document.addEventListener("botshield:success", ({ detail }) => {
    // detail.token (Live) or detail.request_id (Recent Presence), plus detail.turnstile_token
    submitSignup({
      botshield_token: detail.token,
      botshield_request_id: detail.request_id,
      turnstile_token: detail.turnstile_token,
    });
  });
</script>
```

Then on the server: validate the Turnstile token with Cloudflare, verify the BotShield result with BotShield (see [Verify on your server](/gate/verify-on-your-server)), and continue only if both pass. A Turnstile token is valid for 300 seconds and can be validated once.

## What it is not

* **Not a score.** BotShield does not read, weigh, or store the Turnstile result. The gate's answer is the human check.
* **Not required.** Every BotShield Gate works without it.
* **Not a substitute.** Turnstile tells you a browser session looks human enough to proceed. BotShield Gate tells you a real person confirmed, with a signed result your server can check.

## Next steps

<CardGroup cols={2}>
  <Card title="Verify on your server" icon="server" href="/gate/verify-on-your-server">
    Check the BotShield result before you trust it.
  </Card>

  <Card title="Web component" icon="code" href="/gate/web-component">
    Every attribute and event of `<botshield-verify>`.
  </Card>

  <Card title="Result states" icon="circle-check" href="/concepts/result-states">
    What Verified and Unavailable mean, and why there is no third state.
  </Card>

  <Card title="Place a gate" icon="location-dot" href="/gate/place-a-gate">
    Create the gate whose key goes in `scope`.
  </Card>
</CardGroup>
