1
0 Comments

I Tried Protecting My Contact Form Without Adding CAPTCHA

https://cordinant.com/blog/reduce-contact-form-bot-spam-without-captcha

When bot submissions started appearing through the contact form on Cordinant, the obvious option was to add CAPTCHA.

I wasn't convinced that should be the first response.

For a small product website, I wanted the normal experience to stay simple: open the contact page, write a message, send it. I didn't want every legitimate visitor to go through another third-party check unless the traffic actually justified it.

So I approached the problem from the server side instead.

The form now has several small checks rather than one big anti-bot mechanism.

There is a hidden honeypot field that a human should never fill. The server records when the form was opened, so an almost instant submission becomes suspicious. A CSRF token makes a completely blind POST request slightly harder. The submitted values are validated again in PHP rather than trusting browser validation.

Then there is rate limiting.

That became important when I thought about a bot that was slightly smarter. It could GET the real form, preserve the session, leave the honeypot empty, wait five seconds and then POST valid-looking data.

At that point, the early checks may all pass. But repeatedly sending messages from the same source creates another signal.

The part I found interesting is that none of these checks is particularly sophisticated by itself. The useful part is the combination.

SMTP also comes last. A request has to pass the cheap checks before the application is allowed to open an authenticated SMTP connection and send anything to my inbox.

I still wouldn't say this is a universal replacement for CAPTCHA. If the traffic changes, something like Cloudflare Turnstile could become the next layer.

For now, I prefer starting with protection that normal visitors don't notice.

Optional further reading: https://cordinant.com/blog/reduce-contact-form-bot-spam-without-captcha

What has worked best for you on smaller sites: invisible server-side checks first, or adding something like Turnstile from the beginning?

submitted this linkon September 4, 2026