Using Cloudflare Turnstile on a Astro form
Here’s how I used Cloudflare Turnstile on a Astro form to prevent spam and form submission abuse.
Set Turnstile up in the Cloudflare panel first, and grab the TURNSTILE_SITE_KEY
and TURNSTILE_SITE_SECRET
variables, put them in .env
or anywhere you manage env vars.
Then in the Astro component:
<script
is:inline
src='https://challenges.cloudflare.com/turnstile/v0/api.js'
defer
async></script>
<form method='post'>
...
<div
class='cf-turnstile'
data-sitekey={import.meta.env
.TURNSTILE_SITE_KEY ||
process.env.TURNSTILE_SITE_KEY}>
</div>
<input
type='submit'
value='Login'
/>
</form>
On the server endpoint (might be same page, or not):
export async function processTurnstile(
cf_turnstile_response: string
) {
const url =
'https://challenges.cloudflare.com/turnstile/v0/siteverify'
const requestBody = new URLSearchParams({
secret:
import.meta.env.TURNSTILE_SITE_SECRET ||
process.env.TURNSTILE_SITE_SECRET,
response: cf_turnstile_response
})
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: requestBody.toString()
})
const data = await response.json()
return data.success
}
if (Astro.request.method === 'POST') {
const formData = await Astro.request.formData()
const email = formData.get('email')?.toString() || ''
const password =
formData.get('password')?.toString() || ''
const is_valid_turnstile = await processTurnstile(
formData.get('cf-turnstile-response')?.toString() || ''
)
if (!is_valid_turnstile) {
console.log('Invalid turnstile')
} else {
//valid, do something
}
}
→ Here's my latest YouTube video
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025