If your website is returning a 403 error when users try to submit a form protected by Cloudflare Turnstile, it usually means the server-side verification of the Turnstile token is failing or missing. This guide walks you through the legitimate fixes—no token faking or workarounds—so your forms work reliably while keeping Cloudflare’s bot protection intact.
What You’ll Need
- Access to your website’s server-side code (PHP, Node.js, Python, etc.)
- Your Cloudflare account login
- The Turnstile Site Key and Secret Key (found in your Cloudflare dashboard)
- A way to test API requests (like Postman, curl, or a simple test script)
Step-by-Step
- Verify your Turnstile keys are correct. Log in to your Cloudflare dashboard, go to Turnstile, and confirm the Site Key and Secret Key match the ones in your code. A common cause of 403 is using a test key in production or vice versa.
- Check that you’re sending the token to your server. The Turnstile widget generates a token in the browser. Make sure your form includes a hidden input field that captures this token and submits it with the form data. Inspect your form’s HTML or JavaScript to confirm the token is being sent.
- Validate the token server-side. After receiving the token, your server must send a POST request to
https://challenges.cloudflare.com/turnstile/v0/siteverifywith your secret key and the token. Here’s a minimal example in PHP:$secret = 'YOUR_SECRET_KEY'; $token = $_POST['cf-turnstile-response']; $response = file_get_contents('https://challenges.cloudflare.com/turnstile/v0/siteverify', false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => 'Content-Type: application/x-www-form-urlencoded', 'content' => http_build_query(['secret' => $secret, 'response' => $token]) ] ])); $result = json_decode($response, true); if (!$result['success']) { http_response_code(403); exit('Verification failed'); }For Node.js, use
fetchor Axios to POST the same data. - Handle the siteverify response correctly. The API returns a JSON object with a
successboolean. Ifsuccessis false, do not proceed—return a 403 or an error message. If true, continue processing the form. Make sure you’re not accidentally ignoring the response or checking the wrong field. - Check for missing or empty token. If the token is empty or missing, the siteverify will fail. In your server code, explicitly check that
$_POST['cf-turnstile-response'](or your equivalent) is not empty before calling siteverify. If it’s empty, return a 403 with a clear message. - Ensure your server can make outbound HTTPS requests. The siteverify call requires your server to reach Cloudflare’s API. If your server is behind a firewall or has disabled
file_get_contentsorcurl, the verification will fail. Test with a simple script that calls the API and logs the response. - Check for IP address mismatches. Turnstile siteverify can optionally include the user’s IP address. If you pass it, make sure it’s the correct client IP (e.g., from
$_SERVER['REMOTE_ADDR']) and not a proxy IP. If you’re behind a CDN, you may need to use theCF-Connecting-IPheader. - Review Cloudflare Turnstile settings. In your Cloudflare dashboard, check the Turnstile widget’s “Security Level� and “Pre-clearance� settings. If the widget is set to “Invisible� or “Non-interactive�, it may behave differently. Ensure the widget is properly loaded on your page.
- Test with a fresh token. Tokens are single-use and expire after a few minutes. If you’re testing by reusing a token, it will fail. Always generate a new token by reloading the page.
- Clear your browser cache and cookies. Sometimes a stale widget state causes issues. Hard refresh the page (Ctrl+F5) and try again.
Common Mistakes
- Using the wrong secret key. Double-check that you’re not using the Site Key as the Secret Key, or mixing up keys between different domains.
- Not sending the token at all. If your form doesn’t include the hidden field, the server gets nothing and returns 403.
- Verifying on the client side only. Client-side verification is not secure; you must verify on the server. A 403 often appears when the server rejects a token that was never verified.
- Ignoring the siteverify response. Some developers check only if the HTTP request succeeded, not if
successis true. Always check the JSON body. - Hardcoding tokens in code. Never hardcode a token for testing; always generate fresh ones.
Tips
- Use a library or SDK if available for your language—Cloudflare provides official client libraries for PHP, Node.js, Python, and others. They handle the request and response parsing for you.
- Log the siteverify response to a file or console during development. This helps you see the exact error code (e.g.,
invalid-input-responseortimeout-or-duplicate). - Set a reasonable timeout for the siteverify request (e.g., 5 seconds) so your form doesn’t hang if Cloudflare is slow.
- If you’re behind a reverse proxy, make sure your server sees the correct client IP. Turnstile can use the IP to block suspicious traffic, but a wrong IP can cause false negatives.
- Test with the Turnstile test keys provided by Cloudflare (they always pass), then switch to your real keys.
Troubleshooting
If you’ve followed the steps and still get 403, try these:
- Check the error code in the siteverify response. The API returns an
error-codesarray. Common codes includemissing-input-secret,invalid-input-secret,missing-input-response,invalid-input-response, andtimeout-or-duplicate. Each points to a specific fix. - Test the siteverify endpoint manually. Use curl to send a POST with your secret and a token from a live form. If it returns
success: false, the issue is with your token or secret. If it returnssuccess: true, the issue is in your server code. - Check for SSL/TLS issues. If your server has an outdated CA bundle, the HTTPS request to Cloudflare may fail silently. Update your server’s CA certificates.
- Verify that your form’s action URL is correct. If the form submits to a different endpoint than the one with the verification code, you’ll get a 403.
- Try a different browser or incognito mode. Sometimes browser extensions or cached scripts interfere with Turnstile loading.
Frequently Asked Questions
Why does Turnstile return a 403 even when the token is valid?
If the token is valid but you still get a 403, the problem is likely in your server-side code—maybe you’re not checking the success field correctly, or you’re using the wrong secret key. Double-check the siteverify response.
Can I use the same token twice?
No, Turnstile tokens are single-use. Once verified, they cannot be reused. If you submit the form twice with the same token, the second attempt will fail with timeout-or-duplicate.
What is the difference between the Site Key and Secret Key?
The Site Key is public and used in your HTML to load the widget. The Secret Key is private and used only on your server to verify the token. Never expose the Secret Key in client-side code.
Do I need to verify Turnstile on the server if I have it on the client?
Yes, absolutely. Client-side verification is cosmetic; anyone can bypass it. Server-side verification is what actually protects your forms from bots.
How long is a Turnstile token valid?
Tokens are valid for about 5 minutes from generation. If your form takes longer to fill out, the token may expire, and the user will need to complete the challenge again.
Can I skip Turnstile for logged-in users?
Yes, you can conditionally skip the widget for authenticated users if your site has a login system. However, be aware that bots can sometimes create accounts, so consider additional protections.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Mac Start Up Automatically After Power Failure?
- Expose Self-Hosted Service Securely Without Cloudflare Tunnel
- Nano Delivery for Functional Foods: What It Can and Can’t Do4.00
- Disable Apple Watch Turn-by-Turn Vibration While Driving
- Phishing Detector 96.70% Accuracy: What It Means
- Protect Women Athletes’ Privacy on Social Media