The short answer first. Put a honeypot on every form, because it is free for everyone involved. Filter content on the server, because that is the only thing that catches spam written by people. Add a captcha only to a form that is still being hit by bots after that, and when you do, use Cloudflare Turnstile unless you have a specific reason not to. The rest of this post is the reasoning, the trade-offs and the prices, checked on each vendor's own pages on 24 October 2026.
They answer different questions
A honeypot asks: did you fill in a field no person could see? A captcha asks: can you show that a real browser, driven by a person, is sending this? Neither asks the question you actually care about, which is whether the message is junk. That gap matters, because plenty of contact form spam is pasted in by people, and a person passes both tests without trying.
The honeypot
A hidden input that scripts fill and people do not. If the submission arrives with that field filled in, the server drops it. Visitor cost: none. Privacy cost: none, because nothing leaves your page. Accessibility: fine, provided the field is hidden from screen readers and keyboards as well as from eyes. Price: free, since it is a few lines of markup and one check on the server.
<div aria-hidden="true" style="position:absolute; left:-9999px;"> <label for="hp-field">Leave this field empty</label> <input type="text" id="hp-field" name="_gotcha" tabindex="-1" autocomplete="off"> </div>
What it stops: form-filling scripts that read your HTML and post into every input, the cheap, high-volume end of form spam. What it does not stop: anything that renders the page, whether a headless browser or a person. Its one real failure mode is autofill. Name the field something a browser recognises, such as email or phone, and a password manager may fill it in for a genuine visitor, whose message is then dropped without anyone knowing. Use a name that means nothing and set autocomplete="off".
On WebForms the honeypot is the _gotcha field in the snippet above. A submission that fills it is rejected before it is stored, and the sender gets the normal success response, so a bot never learns which field gave it away.
reCAPTCHA v2 and v3
Google's reCAPTCHA comes in two generations that behave very differently. Version 2 is the familiar "I'm not a robot" checkbox, which either passes the visitor straight through or gives them an image challenge, plus an invisible variant that runs when they press your submit button. Version 3 never shows anything: it returns a score from 0.0 (very likely a bot) to 1.0 (very likely a good interaction) and leaves the decision to you. Google suggests 0.5 as a default threshold, and recommends running it in the background of your other pages too, so it has more context to judge by.
That last recommendation is the privacy question in miniature. reCAPTCHA sets a cookie, _GRECAPTCHA, for its risk analysis, and the more pages it runs on, the more it sees. On 2 April 2026 Google changed its terms so that it now acts as a processor for site owners rather than deciding for itself what the data is used for, but the cookie remains, and the French regulator's cookie guidance still names reCAPTCHA as its example of a provider that reuses data for its own purposes, which is the case in which it says a captcha needs consent. Our post on reCAPTCHA alternatives and GDPR goes through the sources.
Accessibility: v2's image challenges come with an audio alternative, and the W3C's note on captcha accessibility explains why that is a weak substitute. The audio is itself distorted to defeat speech recognition, which makes it hard for people to hear too, and visitors who are deaf-blind, or simply somewhere noisy, are shut out. v3 has no challenge to fail, but a low score has to go somewhere. If your server just refuses it, a real person with an unusual browser setup has no way through at all. Google's own advice is to act on low scores behind the scenes, for example by sending the message for moderation, instead of blocking.
Cost: reCAPTCHA is now part of Google Cloud Fraud Defense. The Essentials tier is free up to 10,000 assessments a calendar month, counted per organisation across all your sites, and needs a valid billing instrument in Google Cloud. The Premium tier is also free up to 10,000, then a flat $8 for a month between 10,001 and 100,000 assessments, then $1 per 1,000.
hCaptcha
hCaptcha positions itself as a drop-in replacement: its documentation says it is API-compatible with reCAPTCHA v2, so switching is mostly a change of script and keys, and the token arrives in a field called h-captcha-response. The free Basic plan has no request cap stated on the pricing page. The low-friction modes, where most visitors see no challenge, are paid: a "99.9% passive" mode on Pro ($99 a month billed yearly, or $139 month to month, with 100,000 evaluations included and $0.99 per 1,000 after that) and a fully passive no-captcha mode on Enterprise.
Privacy: hCaptcha is run by Intuition Machines, a US company. It describes itself as a processor for the sites that use it, says it does not sell or rent end-user data, and relies on the EU-US Data Privacy Framework and standard contractual clauses for transfers; keeping EU data in the EU is an Enterprise option. Accessibility: sites can enable a text-based alternative challenge, and visitors can register for an accessibility cookie, which means signing up with an email address and refreshing it by logging in periodically. hCaptcha states WCAG 2.2 AA compliance.
Cloudflare Turnstile
Turnstile runs a series of small, non-interactive JavaScript challenges in the browser (proof-of-work, probing for browser APIs and quirks) and, in its recommended Managed mode, decides per visitor whether to show a checkbox at all. There are no image puzzles; the most it asks for is a tick. There are also non-interactive and fully invisible modes, and it does not require your site to run through Cloudflare. The free plan allows up to 20 widgets, 10 hostnames per widget and no cap on the number of challenges; Enterprise adds any-hostname widgets and longer analytics.
Privacy: Cloudflare's Turnstile privacy addendum lists what it collects (the visitor's IP address, TLS fingerprint, User-Agent header, and the sitekey and origin) and says Cloudflare acts as a processor for your site, and as a controller, on legitimate interests, when it uses that data to improve its bot detection. Its documentation also says Turnstile does not access, store or transmit form entries or other page inputs. Accessibility: Cloudflare states WCAG 2.2 AA compliance.
Side by side
- Visitor friction. Honeypot: none. Turnstile: none, or a checkbox in Managed mode. reCAPTCHA v3: none. reCAPTCHA v2: a checkbox and sometimes image challenges. hCaptcha: the challenge widget on the free plan; the passive modes are paid.
- Accessibility. Honeypot: fine if it is hidden from assistive technology too. Turnstile and hCaptcha: both state WCAG 2.2 AA compliance, and hCaptcha's fullest accessibility route needs an email signup. reCAPTCHA v2: an audio fallback for its image challenges. reCAPTCHA v3: nothing to fail, but what happens to a low score is up to you.
- Privacy. Honeypot: nothing leaves your page. Turnstile: IP address, TLS fingerprint, User-Agent and origin go to Cloudflare. hCaptcha: data goes to a US company acting as processor. reCAPTCHA: a cookie, and French regulator guidance that still cites it as an example of data reuse that needs consent.
- Cost today. Honeypot: free. Turnstile: free, no challenge cap, 20 widgets. hCaptcha: free Basic plan; Pro from $99 a month billed yearly. reCAPTCHA: free up to 10,000 assessments a month across your organisation, with billing details on file; on the Premium tier, $8 flat up to 100,000.
- What it stops. Honeypot: scripts that never render the page. All three captchas: scripted browsers that cannot pass the challenge or score badly. None of them: a person pasting a pitch.
What no captcha stops
A captcha proves there is a person, or a convincing browser, at the keyboard. The agency pitch that arrives through your contact form every Tuesday was typed by a person at a keyboard. Captchas and honeypots are both blind to it, because neither reads the message. That part of contact form spam is only reachable by content filtering: a per-form keyword list for the pitch that repeats itself word for word, and a classifier that reads the text the way you would. That is also why WebForms filters spam without a captcha: the content checks and an AI classifier run on every plan, you add your own blocked keywords per form, and visitors have nothing to solve. A captcha taxes every visitor to stop one kind of sender, and we would rather not make that the default for anyone. The whole stack is in our guide to stopping contact form spam.
Server-side verification is the part people skip
Whichever captcha you choose, the widget in the browser proves nothing on its own. It produces a token, and your server has to send that token, with your secret key, to the provider's verification endpoint and act on the answer. Cloudflare's documentation is blunt about why: tokens can be forged, they expire (Turnstile's after 300 seconds, reCAPTCHA's after two minutes) and each can be verified only once. A form that shows the widget but never checks the token is decoration.
// Never in the browser: the secret key must stay private.
const form = await request.formData();
const res = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
method: "POST",
body: new URLSearchParams({
secret: process.env.TURNSTILE_SECRET,
response: String(form.get("cf-turnstile-response") ?? ""),
}),
});
const result = await res.json();
if (!result.success) {
// Missing, expired, reused or forged token: do not accept the submission.
}The same shape works for hCaptcha (the h-captcha-response field, verified at api.hcaptcha.com/siteverify) and reCAPTCHA (g-recaptcha-response, verified at www.google.com/recaptcha/api/siteverify). Check more than success where the provider gives you more: the hostname, so a token minted on another site is refused, and for reCAPTCHA v3 the action name and the score, because success only tells you the token is valid. The score is a separate field, and acting on it is your job.
One trade-off comes with every captcha. If the provider cannot be reached, your server cannot verify the token, and you have to choose between rejecting the submission, which turns the provider's outage into your form's outage, and accepting it unverified, which turns the captcha off whenever it is down. Decide which before it happens. It is one more reason to keep a captcha for the forms that need it.
The recommendation
- Every form: a honeypot with a meaningless name, hidden properly. It costs nothing and removes the bulk of scripted junk.
- Every form: content filtering on the server, because it is the only layer that reads the message. A form backend that does it for you is the easy route; if you run your own handler, a keyword list and a check for messages stuffed with links are a reasonable start.
- A form still hit by bots after that: Cloudflare Turnstile in Managed mode. It is free with no challenge cap, the most it asks of a visitor is a checkbox, Cloudflare states WCAG 2.2 AA compliance, and its documentation says it does not touch form entries.
- hCaptcha if you are replacing reCAPTCHA v2 and want the smallest code change, or need its enterprise options. Budget for Pro if you want the passive mode.
- reCAPTCHA if you already work in Google Cloud and have settled the consent question for your visitors. With v3, decide what a low score does before you deploy it, and check the score and the action name on the server.
Prices, plan limits and documentation quoted here were checked on each vendor's own pages on 24 October 2026. Vendors change these often, and their pages are the source of truth.