← All posts
Original data

My contact form was cold-emailing strangers for three days

2026-07-28 · 4 min read

In late July my contact form started getting submissions that filled in every visible field and left the hidden honeypot alone. About twenty-three of them over three days. The honeypot had been doing its job for months and had suddenly stopped catching anything.

Annoying, but not the interesting part. The interesting part is what my own form was doing in response.

The actual problem was outbound, not inbound

The form sends a confirmation email to whatever address was submitted. That is normal, polite behaviour: someone contacts you, you confirm you got it.

Except these submissions were not using throwaway addresses. Several were real addresses belonging to real people at real companies, presumably harvested from somewhere. So for three days my domain was sending unsolicited mail to strangers who had never heard of me, from my sending domain, at a steady clip.

That is not an inbox problem. That is the path to spam complaints, and enough of those degrade your sending reputation until the replies you actually care about start landing in junk. I was one week from wondering why my leads had gone quiet.

What the traffic looked like

Every submission had the same fingerprint. Random-case tokens in the free-text fields:

name:    geDIFfqczQeSpJrWdODgibMZ
company: dOGnwNmEwHXEqJpoTL
message: SzbqWBwKmWvIcqLucGDITN
email:   uy.oh.i.h.u.1.0.6@gmail.com

Two things stood out. The tokens flip between upper and lower case far more often than real words do, and the email local parts were heavily dot-aliased, which is a Gmail trick for generating unlimited unique addresses that all deliver to one inbox.

What I built, and the mistake I made first

The fix was to score the content before sending anything, rather than adding one more rule. Signals I ended up using:

  • Case-switch rate above 0.3 on tokens of 12 or more characters. Real names top out around 0.25.
  • Vowel ratio across the message.
  • Vowelless words of four or more letters.
  • Heavily dot-aliased email local parts.
  • A narrow list of outbound-pitch phrases.
  • Time since the form was rendered. A submission under 1.5 seconds blocks on its own.

Scored rather than rule-based, so no single weak signal can reject a real lead by itself.

My first version was too aggressive and I nearly shipped it. It rejected "CRM to QBO sync?" and "HVAC PM scheduling plus SMS reminders". Those are not spam. That is precisely how my actual buyers write: acronym-dense, terse, no vowels to speak of. The vowel check now only judges text over 40 letters, and three-letter acronyms are excluded from the vowelless count.

That is the lesson I would keep if I forgot everything else here. A false positive costs enormously more than a miss. A spam message I let through wastes ten seconds of my time. A real lead I silently reject is a customer I never learn existed.

Two things worth stealing

Drop spam behind a success response. The form returns a normal 200 and shows the usual thank-you, then sends nothing. A 400 error teaches a bot to retry differently. A 200 makes it stop caring. There is no reason to give an attacker a feedback signal.

Test the scorer, not the endpoint. I probed my production endpoint early on and sent myself a real lead email, because the route loads its mail credentials from the environment regardless of what flags I thought I had set. Test the pure function.

I now have 29 test cases: adversarial spam variants plus deliberately awkward genuine messages, including Polish, Czech and German names, acronym-heavy operations questions, and a prospect legitimately asking about SEO. The suite exits non-zero on regression, so a future retune cannot quietly start eating leads.

Replaying spam you already caught proves nothing. The adversarial cases I wrote by hand found three real holes in my first version.

If your form does this too

Check one thing today: does your contact form send a confirmation email to the submitted address before validating anything? If so, your form can be pointed at any address on the internet by anyone, and your domain wears the consequences.

That is the sort of quiet failure I spend most of my time finding in other people's systems. If you want a look at yours, the free 20 minute process audit covers the plumbing as well as the busywork.

08 / Start here

Find your worst bottleneck. Free.

A 20 minute call. We map where your week goes and pick out the first process worth automating. You keep the map either way, and there is no deck to sit through at the end.

Email

pgorski@newfacedesign.com

Phone

+1 (773) 627-2176

Based in

Chicago area

Working with clients everywhere