- #web-security
- #building-in-public
- #forms
Building a contact form that doesn't get spammed
Three layers of defense, each cheap, each measurable.
I just shipped the contact form on aldowebsitellc.xyz. Here's how I'm keeping it from drowning in spam without slapping a CAPTCHA on it.
Layer 1: schema validation. Reject malformed input at the API route, not the database. Email regex, max length on body (5,000 chars), required fields. Bots that probe random endpoints fail here.
Layer 2: rate limit by IP. 5 submissions per IP per hour. Stored in-memory for now (single Vercel region), upgradeable to Upstash Redis later. A real human types one message; a script types fifty.
Layer 3: honeypot field (next).
Add a hidden input named something like website_url. Real users don't see it; bots filling every field do. If it's non-empty on submit, drop the request silently. No CAPTCHA, no friction.
What I'm not doing yet:
- reCAPTCHA — degrades UX, especially on mobile
- Akismet — overkill for this volume
- Email verification (double opt-in) — friction kills conversion
Measure first. I'll watch the contact_submissions table for a week. If spam gets through layers 1+2, layer 3 ships. If layer 3 isn't enough, then we add a third-party tool.
Ship the simplest defense that could work. Add complexity when reality forces it.