Monday morning, the inbox tied to your contact form holds forty new "leads". Thirty-eight are link-stuffed pitches for SEO packages and crypto, one is a string of random characters, and one is a real person asking for a quote on Friday. By the time anyone finds that one, the person has already booked someone else. The instinct is to bolt a picture puzzle onto the form. That usually trades a spam problem for a conversion problem: the bots that matter get through anyway, and the humans who were already on the fence give up on the traffic lights.
Why form spam costs more than an annoying inbox
Spam submissions are not harmless noise. They flow into the same places real leads do, and each place pays a price.
- Your CRM fills with fake contacts. Lead scoring, pipeline reports and source attribution all get quietly worse, because junk records count as leads from whatever channel the bot happened to land on.
- Your autoresponder mails strangers. If the form triggers a confirmation email, a bot can type someone else's address into it. That is how subscription bombing works, and the bounces and spam complaints land on your sending domain's reputation.
- Your team stops trusting the channel. Once people learn the form inbox is mostly garbage, they check it less often, and the real enquiry waits longer.
- Your ad platforms learn the wrong lesson. If a form submission is a conversion event, spam submissions teach the bidding algorithm to find more of whatever produced them.
The problem with puzzle CAPTCHAs
Image and text CAPTCHAs put the whole burden on the visitor. Every legitimate person pays the friction tax so the form can filter out the illegitimate ones, and the tax is highest for exactly the people you least want to lose: someone on a phone, someone in a hurry, someone using a screen reader. The W3C has documented the accessibility problems with CAPTCHA for years in its note on the inaccessibility of CAPTCHA. Meanwhile, determined spam operations route puzzles to solving services, so the visitors who struggle are more often your customers than the bots.
The better model is layered and mostly invisible: several cheap checks that a real person never notices, each catching a different kind of bot, with a visible challenge reserved for the rare submission that looks suspicious.
Layer one: a honeypot field
A honeypot is an extra input that humans never see and simple bots fill in anyway, because they fill every field they find. If the field arrives with a value, the submission is almost certainly automated.
- Hide it with CSS positioned off-screen, not with type="hidden" — many bots skip hidden inputs precisely because honeypots used to rely on them.
- Give it a name that does not look like a real field. A honeypot called "email2" or "phone" will get filled by browser autofill and password managers, and you will reject real people.
- Add tabindex="-1" and autocomplete="off", and mark its wrapper aria-hidden="true", so keyboard and screen-reader users never land on it.
- Reject quietly. Return the same success page a real submission gets, so the bot operator has no signal that anything was caught.
Layer two: a time trap
People take a while to read a form and type into it. Scripts post instantly. Record when the form was rendered and compare it with when it was submitted; anything faster than a person could plausibly type gets flagged.
Do not trust a timestamp sitting in a plain hidden field, because a bot can simply rewrite it. Sign it on the server — a timestamp plus an HMAC of that timestamp with a secret key — and verify the signature on submit. Set the threshold conservatively. Browser autofill can complete a short form very quickly, so a time trap should flag, not hard-reject, anything close to the line.
Layer three: validate on the server, not only in the browser
Client-side validation is a courtesy for humans. Bots rarely run your JavaScript at all; they post straight to the form endpoint. Every rule that matters has to be enforced again on the server.
- Required fields are present and within sane lengths. A "name" field holding several paragraphs is not a name.
- The email address is syntactically valid and its domain has mail exchanger records. A domain that cannot receive mail cannot be a lead.
- Free-text fields are checked for link stuffing. A first enquiry rarely needs several URLs, so a message full of links deserves a closer look.
- The request actually came from your page: check the Origin header and use a CSRF token, so the endpoint cannot be hammered from anywhere on the internet.
- Submissions are rate-limited per IP address and per email address, so one source cannot flood the form in a burst.
Layer four: an invisible challenge, verified server-side
For forms that attract more sophisticated bots, add a challenge service that usually runs without asking the visitor to do anything. Cloudflare Turnstile, reCAPTCHA v3 and Friendly Captcha all work on this principle: the browser collects signals in the background and hands your form a token. Only a small share of visitors, the ones that look automated, see anything interactive.
The part people skip is the one that makes it work. The token is worthless until your server sends it to the provider's verification endpoint and gets a success response back. A widget that renders on the page but is never verified server-side stops nothing, because a bot posting directly to your endpoint never loads the widget in the first place. Tokens are also single-use and expire after a short window, so a visitor who leaves a form open for a long time needs a fresh one rather than a hard failure.
One more practical trap: if your site sends a Content Security Policy header, the challenge script and its iframe have to be allowed in it. A blocked challenge script means no token, and depending on how you coded the fallback, either every submission fails or none of them is protected.
Quarantine instead of delete
No filter is perfect, and the cost of a false positive is a lost customer who will never tell you they tried. So do not delete anything that fails a soft check. Route it somewhere reviewable instead.
- Hard failures — a filled honeypot, a missing CSRF token, a failed challenge verification — can be dropped without review.
- Soft failures — a very fast submission, several links, an unusual email domain — go to a quarantine folder or a tagged status in the CRM, with no autoresponder sent.
- Someone skims the quarantine on a schedule. Real leads get released into the normal flow; the rest is cleared.
- Whatever gets released tells you which rule is too aggressive, so you can loosen that one rule instead of the whole system.
Measure it like a conversion problem
Spam protection is a trade-off, and you can only manage a trade-off you can see. Track form starts, completed submissions, hard rejections and quarantined submissions as separate numbers. If completions drop right after you add a new check, the check is catching people. If quarantine keeps filling with obvious junk that clears on every review, a soft rule can safely be promoted to a hard one.
Also keep an eye on the autoresponder's bounce and complaint numbers after any change. They are the earliest sign that fake addresses are still getting through and being mailed.
A sensible default stack
For most small-business contact and quote forms, this combination catches the overwhelming majority of automated spam while staying invisible to real visitors:
- A well-named, CSS-hidden honeypot with a silent success response
- A signed render timestamp that flags implausibly fast submissions
- Server-side validation, an Origin or CSRF check, MX lookup and per-IP rate limits
- An invisible challenge verified on the server, allowed in your Content Security Policy
- A quarantine for soft failures, reviewed on a schedule, with no confirmation email until a submission is cleared
None of these layers asks a customer to prove they are human. That is the point. The form's job is to make it easy for the person asking about Friday to reach you — the filtering should happen behind the page, where only the bots ever feel it.
Does your business show up when AI answers?
ChatGPT, Claude, Perplexity and Google's AI Overviews are already answering the questions your customers ask. The $49 AI Visibility Scan shows you where you're cited, where you're invisible, and the three changes that move you first — a written report in your inbox within 48 hours. If nothing in it is actionable, you don't pay.
Run the $49 AI Visibility Scan →Share this article
Comments
Leave a comment