form-basics

Contact form with captcha: stopping spam without losing real people

October 1, 2026 ・ Halict Editorial

The form has been live for a few months and the notification inbox has turned into a chore. Offers of search engine services, link exchanges in broken English, the same message submitted eleven times in four minutes. Somewhere in the pile is a real enquiry from a real company, and it takes a person twenty minutes every morning to find it. The obvious fix is to bolt a captcha onto the form. That does work, but it is worth being precise about what the check can decide and what it cannot, because the cost of getting it wrong is paid by the visitors who were about to become customers.

What the check is actually being asked to decide

A challenge widget does one narrow job: it produces a token in the visitor's browser, and the backend asks the provider whether that token is good. Nothing about it inspects the message. A bot that solves the challenge gets through. A person who writes a genuine enquiry and fails the challenge does not.

The providers split into two designs, and the split matters more than the brand name.

The first design returns a pass or a fail. Google's reCAPTCHA documentation describes the v2 checkbox as requiring "the user to click a checkbox indicating the user is not a robot", and the v2 invisible variant as being "invoked directly when the user clicks on an existing button on your site", with the note that "by default only the most suspicious traffic will be prompted to solve a captcha".

The second design returns a number. The same documentation describes v3 as verifying "if an interaction is legitimate without any user interaction", returning "a score, giving you the ability to take action in the context of your site", with suggested actions including "requiring additional factors of authentication, sending a post to moderation, or throttling bots".

That difference decides how much work falls on the backend. A pass or fail result can be wired into a submit handler in an afternoon. A score cannot: somebody has to choose a threshold, and then somebody has to decide what happens to the submissions that land under it. Dropping them silently is the easy choice and the worst one, because there is no way to find out later what was thrown away.

Four layers, only one of which is a captcha

A captcha is one of several things that can sit in front of a form, and it is rarely the cheapest place to start.

Layer Cost to the visitor Catches Misses
Hidden field trap None Crude scripts that fill every input Anything that reads the page properly
Rate limit per address None, until they hit it Repeat floods from one source Slow, distributed submissions
Challenge widget A moment, sometimes a puzzle Generic automation at scale A paid human typing into the form
Content classifier None Sales pitches and link spam Anything written to sound ordinary

The hidden field trap, usually called a honeypot, is an input that a browser never shows and a person therefore never fills. Anything arriving with it filled is discarded. It costs a visitor nothing, it is a few lines of code, and on a low traffic contact form it often removes most of the volume on its own. It stops working against automation that renders the page.

A rate limit per network address is the second cheap layer. Eleven identical submissions in four minutes is a pattern worth refusing regardless of what any widget said about the visitor.

A content classifier looks at the message rather than the sender. Akismet is the familiar one, and it counts what it does in spam checks: each time it examines a comment or form submission. Its plans are sold by that volume, with a personal tier priced by the sender, a Pro tier that includes 500 spam checks a month on one site, and a Business tier with 5,000 spam checks a month across unlimited sites. That volume is the thing to check first: a form that receives 3,000 submissions a month does not fit the Pro allowance.

What the widgets cost once traffic grows

Prices below were taken from each provider's own page and describe what is published today.

Provider Free allowance Above the free allowance Visitor experience
reCAPTCHA 1 to 10,000 assessments a month, aggregated per organization across all accounts and sites 10,001 to 100,000 assessments: $8.00 flat fee. More than 100,000: $1.00 per 1,000 Checkbox, invisible, or scored with no interaction
Cloudflare Turnstile Free plan: up to 20 widgets per account, unlimited challenges, 10 hostnames per widget Enterprise: contact sales. Unlimited widgets, up to 200 hostnames per widget Managed, non-interactive, or invisible
hCaptcha Basic plan at $0 Pro at $139 a month billed monthly, or $99 a month billed annually, including 100K monthly evals, then $0.99 per 1,000 Interactive by default, passive mode on paid plans

Two details in that table are easy to miss. The reCAPTCHA free tier is counted per organization, not per form, so an agency running forms for fifteen clients under one account shares a single allowance of 10,000. And Turnstile's free plan caps widgets at 20 per account rather than capping traffic, which is the opposite trade: a single busy form is cheap, a hundred small ones are not.

Turnstile also differs in what it asks of the visitor. The Cloudflare documentation describes it as working "without showing visitors a CAPTCHA" and says the product "processes only the data strictly necessary to provide this security function", adding that it "does not access, store, or transmit user communications, form entries, or other page inputs". For a form that collects an address or a phone number, that sentence is worth reading twice before choosing.

The server side half that keeps getting skipped

A widget on the page proves nothing. The token it produces has to be sent to the backend, and the backend has to call the provider's verification endpoint before the submission is stored. A form that renders the widget and never verifies the token is decoration: a script posting straight to the endpoint bypasses it entirely.

Two parts of that verification step are routinely left out. The first is the hostname. The verification response says which hostname the token was issued for, and the Turnstile setup documentation is explicit that a backend "must validate the deployment-specific hostname returned by Siteverify" and warns against allowing local hostnames in production. Skip that check and a token minted on a copy of the form running on someone else's machine is accepted.

The second is what happens when verification itself fails to answer. The provider is a third party and will occasionally time out. If the code treats a timeout as a failed challenge, the form stops accepting anything for the duration, and nobody finds out until a customer telephones. If it treats a timeout as a pass, the gate opens. Either is defensible, but the choice has to be made deliberately and logged, so the pattern is visible afterwards.

The cost of the check falls on the people worth keeping

The W3C group draft note Inaccessibility of CAPTCHA, published in December 2021, is the clearest statement of the problem. On visual challenges it says that "asking users who are blind, visually impaired or dyslexic to identify textual characters in a distorted graphic is asking them to perform a task they are intrinsically least able to accomplish". On the usual audio fallback it reports that "sound output, which is itself distorted to avoid the same programmatic abuse, was unintelligible to all four test subjects", and that audio challenges "impose a cognitive overload to all human users in comparison to the cognitive load necessary to understand normal human speech".

That is the case for preferring a check that stays out of the way. A widget that runs invisibly for most visitors and only escalates for suspicious traffic costs a legitimate visitor nothing. An image grid on every submission costs every visitor something, and the visitors who abandon the form do not write in to complain about it.

There is a quieter failure worth guarding against too. When a challenge fails, a lot of forms show a red line under the widget and nothing else. The visitor who has typed three paragraphs does not know whether to press the button again, whether the message went, or whether the site is broken. The message that appears on failure deserves the same attention as the check itself: say plainly that the check did not pass, keep everything already typed, and give a second route such as an address to mail directly.

What happens to the spam that still arrives

No gate stops everything, so the volume that gets through has to be cheap to dispose of. This is where the choice of tool matters more than the choice of captcha provider. If every submission lands as an identical notification mail in a shared inbox, three people read the same junk pitch, and nobody can tell whether the real enquiry underneath it was answered.

The alternative is to keep submissions as records rather than messages. A list where each response has an owner and a status makes the daily pass fast: mark the obvious junk once, and it leaves the working view for everyone. What remains is a short list of things that need a reply, and the features built around handling a response after it arrives are the part that decides how long that daily pass takes. The same holds for the cases where volume is expected to be high, such as recruitment intake or grant applications, which are worth looking at as separate use cases rather than as one contact form.

One more thing decides how cheap that disposal is: whether the junk that arrives can be answered by nobody at all. A submission with no owner sits in the list looking like work. A submission marked as junk by one person, once, is gone for the whole team. The difference sounds trivial and is not, because the daily cost of a contact form is almost never the gate. It is the repeated reading of the same message by several people who each assume somebody else has dealt with it.

Marking junk also generates the data needed to tune the gate. After a month, a list of what was marked as spam shows whether the pattern is one address submitting repeatedly, which a rate limit handles, or scattered single submissions with the same wording, which a content classifier handles. Guessing at a threshold without that list is how a form ends up rejecting real customers.

What to change first

Add a honeypot field and a per address rate limit this week, since neither costs a visitor anything and both can ship without a provider account. Then pick one widget, wire the verification call on the server including the hostname check, and give the failure message real wording. If the daily pass through submissions is still the expensive part after that, the gate is not the problem, and the place to look is how responses are worked after they land in Halict.

Q1. Will adding a captcha reduce the number of genuine enquiries?

It can, and how much depends entirely on which design is chosen. An invisible or managed widget that only challenges suspicious traffic is close to free for a normal visitor, while an image grid shown to everyone adds a step to every submission. Measure submissions for two weeks before and after so the effect is known rather than assumed.

Q2. Is a honeypot field enough on its own for a small contact form?

Often yes, at least at first. A hidden input that only automation fills removes the crude bulk submitters, costs nothing, and needs no third party account. The reason to add more later is that it does nothing against automation that renders the page properly, which is what arrives once a form has been indexed for a while.

Q3. Do these providers see what visitors type into the form?

That depends on the provider, and the answer belongs in the privacy notice either way. Cloudflare states that Turnstile "does not access, store, or transmit user communications, form entries, or other page inputs". For any provider, the safe assumption is that the check itself is a separate processor that has to be disclosed, alongside whatever the form tool stores.

Q4. What should happen to a submission that fails the check?

Show a clear message, keep everything already typed in the fields, and offer a second route such as a direct mail address. Discarding the submission silently is the choice that causes real damage, because a lost enquiry is invisible: nobody on either side knows it happened.

Q5. Does a scored check like reCAPTCHA v3 need extra work compared with a checkbox?

Yes. A score has to be compared against a threshold that somebody chooses, and then the submissions below that threshold need a destination. Sending them to a review queue rather than deleting them is what makes a threshold safe to adjust later.

Q6. How much spam does a form typically get before this is worth doing?

There is no universal number, but the useful trigger is not volume. It is the moment a person starts spending time every day separating junk from real enquiries, because that cost recurs whether the daily total is ten or two hundred.

All guides