The CAPTCHA is there – but protects nothing
A visible reCAPTCHA, hCaptcha or Turnstile widget says little about whether a form is protected. Here are the typical misconfigurations and how to check your own forms in minutes.
Many website owners add a CAPTCHA to their contact form, see the little badge or checkbox appear, and consider the spam problem solved. Then the junk keeps arriving. Often the CAPTCHA itself is fine – it is just not connected to anything that could stop a bot. This post walks through the typical ways reCAPTCHA, hCaptcha and Cloudflare Turnstile end up as decoration, and how to check your own forms.
Where the protection actually happens
All three services work on the same principle. A script in the browser runs its checks and hands the form a token – a short string that says “this visitor passed”. The token travels with the form submission to your server. Only there does the real decision happen: your server sends the token together with a secret key to the provider and gets back an answer – success or failure, and for reCAPTCHA v3 also a score. If any link in this chain is missing, the widget still looks perfectly normal, but a bot that posts directly to your form handler goes straight through.
The test key that waves everything through
Google, hCaptcha and Cloudflare publish special test keys so developers can run automated tests without solving puzzles. With the matching test secret, verification always succeeds – that is their whole purpose. They are meant for local and staging environments, but they regularly survive the move to production: a developer pastes them in during the build, the site goes live, and nobody switches them out. reCAPTCHA at least shows a notice in the widget saying it is for testing only, but with an invisible setup hardly anyone looks. If the test site key and test secret are both live, every submission passes, whether it comes from a human or a bot; if only the site key is a test key, verification fails instead and real messages may be rejected.
A token that nobody checks
The second classic is a server that never asks the provider. The widget runs, a token is created and submitted, and then the form handler simply ignores it. Typical sources are hand-written forms, copied snippets that only cover the browser side, and half-finished plugin settings. A close relative: the server does call the verification service but only checks that the request went through, not what the answer says. Or the score threshold for reCAPTCHA v3 is set to 0, which accepts every score the service can return. Some handlers also “fail open”: if the token is missing or the verification fails, they accept the message anyway – and a bot simply leaves the token out.
When the widget never really loads
Other misconfigurations break the browser side instead. If the site key is not registered for the domain – typical after a move from staging to production, a domain change or a new subdomain – the provider refuses to issue tokens and shows an error in the widget or the browser console. After a theme switch or a plugin update, the widget markup may still be in the page while the script that brings it to life is gone. A cookie consent tool can have the same effect if it blocks the CAPTCHA script until the visitor agrees. What happens next depends on the server: if it insists on a valid token, real visitors can no longer send messages and nobody notices the lost enquiries; if it does not, bots pass freely.
Two more cases are easy to miss. With reCAPTCHA v3, the badge in the corner only shows that the script is loaded somewhere on the page – perhaps by the theme or another plugin – while the integration in the form plugin itself is switched off. And caching plugins can store a page together with a token embedded in the HTML. CAPTCHA tokens are only valid for a few minutes and can be verified only once, so a cached token fails for real visitors; the tempting “fix” of turning the check off quietly removes the protection.
- The widget shows a “for testing purposes only” notice
- The v3 badge sits in the corner
- The widget renders and returns a token
- Spam has stopped – but so have real enquiries
- Public test keys are in use and every verification succeeds
- The form plugin’s reCAPTCHA integration is switched off
- The server never sends the token for verification, or ignores the result
- Site key not registered for the domain, or cached tokens expired
A short self-check
- 1Open the form page in a private window, open the browser’s developer tools and look in the console for errors from the CAPTCHA script.
- 2Check that the widget actually appears – once before and once after accepting the cookie banner – and that the form cannot be sent while it is missing.
- 3In the provider’s admin console, check that the site key lists your live domain (including variants such as www), and that the keys in your form plugin or code match it – not keys from documentation or a staging setup.
- 4In the form plugin settings, make sure the CAPTCHA integration is enabled for this specific form; for reCAPTCHA v3, check that the score threshold is above 0.
- 5If you have access to the code, find the server-side verification and check that it rejects the submission when the answer is negative or the token is missing.
- 6Exclude the form page from full-page caching, or make sure the token is created fresh in the browser.
- 7Send a real test message and check that it arrives; repeat the check after changes to the theme, plugins or consent tool.
A CAPTCHA is one layer, not the whole protection. Combined with a honeypot field, a time check and server-side filtering, a misconfiguration in one place does not leave the form wide open. If you want a quick picture of where your forms stand, enter your domain on formexposed.com – the report shows whether the parts of your CAPTCHA setup that are visible from outside are in order.