Turnstile
Protect against bots and abuse with Cloudflare's invisible CAPTCHA alternative. Runs entirely client-side with zero user friction - no ticking boxes or identifying traffic lights.
Setup
Create a Turnstile site in your Cloudflare Dashboard → Turnstile, then configure:
# Public site key (used in frontend)
TURNSTILE_SITE_KEY='0x4AAAAAAaBbCcDdEeFfGg'
# Secret key (used in API for verification)
TURNSTILE_SECRET_KEY='0x4AAAAAAaBbCcDdEeFfGg_SecretKey'How It Works
Turnstile runs a client-side challenge when the user interacts with protected endpoints:
sequenceDiagram
participant User
participant Frontend
participant Turnstile as Turnstile Widget
participant API
participant CF as Cloudflare Verification
User->>Frontend: Click upload
Frontend->>Turnstile: Request challenge
Turnstile->>Turnstile: Run client-side checks<br/>(browser fingerprinting)
Turnstile-->>Frontend: Token (short-lived)
Frontend->>API: POST /v1/images<br/>turnstile_token: xxx
API->>CF: Verify token + secret
CF-->>API: Valid / Invalid
alt Valid
API->>API: Process upload
API-->>User: Success
else Invalid
API-->>User: 403 Bot detected
endThe widget analyses browser behaviour, TLS fingerprints, and other signals to determine if the request is legitimate. Most users never notice it's there.
Where It's Used
Turnstile protects:
- Image uploads - Prevents automated spam uploads
- API key generation - Stops bots creating unlimited keys
- Login attempts - Mitigates credential stuffing attacks (if using Auth.js)
Tokens are single-use and expire after 5 minutes.
Optional
Turnstile is entirely optional. If the environment variables aren't set, the API skips verification and accepts all requests. Useful for development or trusted environments.
To explicitly disable after having it enabled:
# Remove from .env or wrangler.toml
# TURNSTILE_SITE_KEY=''
# TURNSTILE_SECRET_KEY=''Troubleshooting
Widget doesn't appear
Check TURNSTILE_SITE_KEY is set in your frontend environment variables (.env or build config).
"Invalid Turnstile response"
- Verify
TURNSTILE_SECRET_KEYmatches your Cloudflare dashboard - Check the site key domain matches your deployment
- Ensure the token hasn't expired (5 minute lifetime)
- Confirm you're not reusing tokens (single-use only)