Stop contact form spam without punishing visitors
By Nick Peplow ·
Put a contact form on the public internet and bots will find it. Not because your site is popular — scanners crawl the web looking for <form> tags the way port scanners look for open ports. The first junk submission usually lands within days of going live, traffic or no traffic.
So the question isn’t whether you’ll get form spam. It’s how much friction you’re willing to impose on real visitors to stop it — and most sites answer that question backwards, reaching for a CAPTCHA first when cheaper, invisible layers would have done the job.
Why is my contact form getting spammed?
Three motives account for nearly all of it, and knowing which one you’re seeing tells you how hard you’ll have to fight back.
Link-drop SEO spam. The classic payload: a message stuffed with URLs and anchor text, blasted at every form the bot can find. The economics only work if the content ends up somewhere crawlable — a guestbook, a comment feed, a testimonial page — but the bots don’t check first. Sending is so cheap that spraying private inboxes too costs them nothing. This is the high-volume, low-effort majority, and it’s the easiest to stop.
Phishing relays. If your form triggers an autoresponder or a confirmation email, it can be abused as a free mail cannon: the attacker puts a victim’s address in your email field and the phishing payload in the message body, and your domain delivers it. This is the quiet reason form spam matters beyond annoyance — enough relayed junk and your sending reputation takes the hit.
Vulnerability scanners. Submissions full of ' OR 1=1 and <script> tags aren’t trying to reach your inbox at all. They’re fingerprinting your stack for something exploitable. Harmless to a form backend that treats input as data, but they inflate your spam volume and they never stop coming.
The escalation ladder
The principle: order your defenses by what they cost the visitor, not by how powerful they are. Start at the bottom, and climb a rung only when the level below is demonstrably failing — which, for most contact forms, never happens.
| Layer | Visitor friction | Stops | Gets past it |
|---|---|---|---|
| Honeypot | None | Bots that parse HTML and fill every field | Bots running a real browser |
| Rate limiting | None | Floods, retry loops, scripted bursts | Distributed, low-and-slow senders |
| Time trap | None | Bots that submit in under a second | Bots that add a delay |
| Invisible challenge | Near-zero | Most automation, including headless browsers | CAPTCHA-solving farms |
| Visible CAPTCHA | Real | Almost everything automated | Solving farms, again — at a price |
Rung 1: the honeypot. An extra field, hidden from humans with CSS, that bots dutifully fill because they fill everything. Filled field means filtered submission. It’s invisible to visitors and it catches the large majority of automated spam, because the large majority of spam bots are raw HTTP clients that parse your HTML and never render it. The honeypot technique has more depth than the usual tutorial version suggests — field naming, hiding method, and where the check runs all decide whether it holds up.
What slips through: any bot driving a real browser. Puppeteer with a stealth plugin renders your CSS, sees the field is hidden, and skips it.
Rung 2: rate limiting. Per-IP and per-endpoint throttles. A human sends one message; a script in a loop sends four hundred. Rate limits convert an unbounded flood into a trickle, and they’re the layer that protects you from the vulnerability-scanner traffic that a honeypot alone won’t slow down.
What slips through: distributed attacks that send one submission per IP, slowly. Rare against contact forms — that effort level is reserved for signup fraud and credential stuffing, not link drops.
Rung 3: the time trap. Humans take seconds to fill a form; bots submit in milliseconds. Record when the form was rendered — a signed timestamp in a hidden field — and reject submissions that arrive implausibly fast. Zero friction, and it catches the fire-and-forget bots that beat a badly implemented honeypot.
What slips through: any bot that sleeps for five seconds. Trivial to add once the operator notices, which is why time traps are a supplement, never a foundation.
Rung 4: invisible challenges. Cloudflare Turnstile, invisible reCAPTCHA, hCaptcha’s passive mode — the browser proves it’s a real browser through background checks, and the visitor sees at most a brief spinner. This is the first rung that stops headless-browser bots, and the last rung that doesn’t cost you conversions. The three providers differ more than their marketing suggests; the reCAPTCHA vs hCaptcha vs Turnstile comparison covers how each one decides you’re human and what each one collects while deciding.
What slips through: CAPTCHA-solving farms — humans paid fractions of a cent to pass challenges. If someone is paying humans to spam your contact form specifically, you have a different problem than spam filtering solves.
Rung 5: the visible CAPTCHA. Puzzles work. They also make every legitimate visitor pay a tax for the bots’ behavior, and some fraction of real senders abandon rather than pay it. That’s an acceptable trade on a form under targeted attack. It’s a bad trade as a default, which is exactly how most sites deploy it.
Escalate per form, not per site
The rung you need is a property of each form, not of your site. A contact form getting hammered doesn’t mean your newsletter signup needs a CAPTCHA. Keep every form at the lowest rung that holds, and move one form up when its spam actually starts arriving in your inbox — not when you imagine it might.
This is how FormWire ships: honeypot filtering and rate limiting run on every form by default, server-side, and hCaptcha, reCAPTCHA, or Turnstile are per-form toggles you flip only for the form that’s under fire. Flagged submissions land in a spam tab instead of your inbox, so you can verify the filter is right before you trust it.
The goal was never zero spam — bots will keep POSTing whether you filter or not. The goal is zero spam in your inbox at zero cost to the people you actually want to hear from. Climb the ladder only as far as that requires, and no further.