Spam in the contact form: the day a client started getting a hundred fake messages a day
A client who sells custom furniture wrote to us a couple of months ago, alarmed: his contact form notifications had gone from two or three messages a week to over a hundred a day. None of them real, of course, just links to replica watches, crypto offers, the occasional Cyrillic text with a URL shortener behind it. The form had been live since the site launched, almost a year earlier, without a single filter on it.
Spam on a contact form almost never shows up because someone found your specific site: it’s bots crawling the internet for any <form> tag with an action pointing at a submission endpoint, and they fill it in the moment they spot it. That’s why a brand new form can start receiving it within days of going live, with no SEO mistake and no one “singling you out” on purpose.
The first thing we did was check what we were actually running, not what we assumed was configured. On most projects we’ve built over the last couple of years the form posts to Web3Forms — the same service we use on our own site, in fact, the same API we reviewed when we audited our own server — and it already ships with an optional honeypot field and reCAPTCHA out of the box. On the furniture client’s project we’d left it switched off because, at the time, “we’ll turn it on later if we need to.” We never did. It’s not comfortable to admit, but I suspect this isn’t the only project with that kind of checkbox left unticked.
The honeypot that actually stopped the spam
Turning the honeypot on was the first move, and by far the one that cut the most spam: a text field hidden with CSS that a human never sees or fills in, but that a bot crawling the HTML does. One detail that matters: don’t hide it with display:none, which some bots already dodge; it works better pushed off-screen with position:absolute and left:-9999px. If that field comes back filled, Web3Forms’ own backend drops the submission before it ever reaches our inbox. That alone took us from a hundred messages a day down to half a dozen.
The half-dozen that still got through were passing the honeypot, which meant slightly more careful bots. So we added two more filters: a limit of three submissions per hour per IP (generous enough for any real visitor, however insistent), and a silent rejection of any message whose free-text field contained a full http URL. Almost no genuine visitor pastes a whole link into a contact form’s message box; spam bots, on the other hand, always do.
Why we skipped reCAPTCHA
We didn’t turn on reCAPTCHA, and this is an opinion not everyone in the trade shares: the “I’m not a robot” checkbox adds real friction right at the step the client most needs people to finish, and a furniture showroom lives on people actually completing that form, not on proving to Google they’re human. We measured the completed-submission rate before and after the new filters and it didn’t drop a single point, so for now we’re leaving it out there. On projects where spam keeps coming back hard despite the honeypot and the rate limit, we do keep it as a last resort: we needed it with a real-estate client who also had accessibility issues left over from their previous agency, and there reCAPTCHA v2 clashed badly with the screen reader until we switched it to the invisible version.
What stuck with me after this client is that we’d spent months assuming “Web3Forms already filters this” without ever checking, project by project, that the filter was actually switched on. How many other “we’ll turn it on later if we need to” boxes are sitting unticked on sites we haven’t opened since we handed them over?