← back to blog
1 min read
  • #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.