Building a contact form that doesn't drown in spam
You'd think a contact form is solved. It's not — particularly if you're running on a public domain that's been crawled. Within 48h of launching ours, we got our first wave of bot submissions selling things we didn't want.
What works:
1. hCaptcha (or Turnstile)
The frontend renders a captcha widget. The token is included in the request body. The backend validates the token against the provider's siteverify endpoint before processing the form. Without a valid token, the request is rejected — no email sent.
2. Per-IP rate limiting
Five submissions per IP per hour, max. Bots that bypass captcha (rare but happens) hit this ceiling.
3. Header-injection defense
The user-supplied name/email/message are interpolated into the email body. We escape HTML metacharacters, strip CRLF from any field that could land in a header, and use the captcha token as a precondition for the email send. CRLF injection in the “name” field would let an attacker BCC arbitrary recipients; not in our pipeline.
Result: the contact form gets ~3-5 legitimate inquiries per week and 0 bot messages. The ratio matters for support inbox sanity.