Cloudflare Turnstile (CAPTCHA) Quick Setup on a Rails app
As I was creating a landing page for a business, I had to add a contact form that immediately started to receive spams. To mitigate this, I decided to take a look into CAPTCHA systems and landed on two: Google's reCAPTCHA and Cloudflare's Turnstile.
Google's reCAPTCHA seemed to be slightly more cumbersome to implement even having the great recaptcha gem available for Rails. The reason being that there are 2 distinct versions of the captcha system that you need to create in the Google's reCAPTCHA website (v2 is apparently used as a fallback to v3) and, no only it is a bit a confusing (at least to me), it displays a little flag on the bottom right corner of the screen which is quite annoying:
Then, I decided to take a closer look into Cloudflare's Turnstile. Right off the bat, it is WAY more straightforward to implement. After creating an account on their website, it will generate a key for the frontend and a secret key for the backend.
Here's how a simple implementation can go. On your frontend, there are two steps:
- Add their script into your page
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" defer></script>
- Inside your form, add a div with the specific CSS class of
cf-turnstile
and a data attribute ofdata-sitekey="YOUR_CLIENT_SIDE_KEY"
<div class="text-center cf-turnstile" data-sitekey="<%= ENV["CLOUDFRONT_TURNSTILE_SITE_KEY"] %>"></div>
On the backend, wherever you handle the form action triggered when you submit the form, make a POST
request to Cloudflare's site verify endpoint. If you use Rails and the HTTParty gem:
idempotency_key = SecureRandom.uuid
response = HTTParty.post(
"https://challenges.cloudflare.com/turnstile/v0/siteverify",
body: {
secret: ENV["CLOUDFRONT_TURNSTILE_SECRET_KEY"],
response: params["cf-turnstile-response"],
remoteip: request.remote_ip,
idempotency_key: idempotency_key
}
)
result = response.parsed_response
return result["success"] == true && result["idempotency_key"] == idempotency_key
The cf-turnstile-response
is a parameter that Cloudflare injects into your form upon submission and the idempotency_key
is an arbitrary value that you can verify to assert that the challenge request matches the response you got.
And that's it! You can further add this to a method like verify_turnstile
and if that returns true
you can carry on with the usual logic (e.g. send email, register user, etc).