Turnstile foundations

Separate site and secret keys

Publish the site key in browser markup while keeping the secret key only in the server environment.

8 minute lesson

~~~

When you create a Turnstile widget in the Cloudflare dashboard, you get two keys with very different jobs.

The site key identifies the widget and is safe to send to browsers. It sits in your HTML where anyone can read it:

<div class="cf-turnstile" data-sitekey="0x4AAAAAAABkMYinukE8nzKd"></div>

The secret key authenticates Siteverify requests and must stay in a server secret store. On Workers or Pages, that means:

npx wrangler secret put TURNSTILE_SECRET_KEY

The word “key” hides the difference, so keep the rule mechanical: the site key ships to the client, the secret key never does. A leaked site key is harmless. A leaked secret key lets anyone forge “this is a human” verdicts against your backend.

One widget per environment

Use separate widgets for development, staging, and production. Restrict production hostnames in the widget settings and name widgets by purpose, something like contact-form-prod and signup-staging. For local work, configure Cloudflare’s documented test keys instead of a real widget, so your development traffic never touches production analytics.

Verify nothing leaked

Frameworks make leaking easy. In Astro and Vite projects, any environment variable prefixed with PUBLIC_ gets inlined into the client bundle. Name the secret TURNSTILE_SECRET_KEY, not PUBLIC_ANYTHING, and read it only in server code.

After a build, check the output:

npm run build
grep -r "0x4AAAAAAABkMY_your_real_secret" dist/ && echo "LEAKED" || echo "clean"

Confirm the production secret never appears in generated HTML or browser network responses. Open devtools on the deployed form and search the page source and responses for the secret’s first characters.

If a secret appears in source, logs, or client JavaScript, rotate it and investigate every environment that used it. Rotation is cheap. An attacker quietly bypassing your bot protection for months is not.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →