CAPTCHA puts the burden on your visitors. The better approach is five server-side layers, each invisible to a real person, that catch bots before the message ever reaches your inbox. No puzzle, no friction, no third-party widget.
Download it as a prompt file, paste it into Claude, ChatGPT, Gemini or any LLM, and it will walk you through implementing each layer one step at a time.
↓ Download as LLM promptA honeypot is a form field that is hidden from human visitors but visible to bots. Real people never fill it in because they never see it. Bots fill in every field they find, and that is their undoing.
The implementation is simple:
website or url.display:none or position:absolute; left:-9999px. Do not use the hidden attribute, since some bots ignore it.Real people cannot see the field, so they never fill it in. A bot filling in every visible and invisible field triggers an immediate rejection, at zero cost.
A human filling in a contact form takes at least a few seconds: they read the labels, type a message, and click submit. Automated bots often submit in under a second.
The time trap works like this:
This layer catches a different class of bot from the honeypot: scripted submitters that understand hidden fields and leave them empty, but still fill and submit faster than any human could.
The honeypot and time trap stop most single-shot bots. Rate limiting stops the ones that retry or that run in slow-send mode to evade timing checks.
The principle is straightforward: track how many submissions have arrived from each IP address in a rolling time window. Reject any submission from an IP that has already submitted recently.
Some spam bypasses the mechanical filters above. The message arrives at human speed, from a fresh IP, with the honeypot untouched. The giveaway is in the content.
SEO spam and link-building pitches almost always include several URLs. Legitimate enquiries rarely include more than one or two. Setting a maximum number of http:// or https:// links across all fields is a low-false-positive way to catch this category.
A limit of 2 links is enough to stop most link-building spam while allowing a real visitor to share a couple of reference URLs in their message. Set it to -1 to disable the check entirely if your use case routinely involves more links.
A blocklist is a list of phrases that, if found anywhere in the submitted text, cause an automatic rejection. The match is case-insensitive and matches substrings, so casino also matches online casino and Casino Royale.
Common starters for a contact-form blocklist:
bitcoin crypto casino seo services guest post backlink link building rank higher viagra cialis
The list works best when it is tuned to the spam you are actually receiving. Keep it short and specific: an overly long list risks false positives and becomes hard to maintain. Review it every few months and drop anything that is no longer appearing in your spam.
The layers above are all local: they learn from your own submissions. A shared blocklist adds a network dimension. When one site catches and reports a spam IP, every other site using the same blocklist benefits immediately.
The idea is simple in principle:
The value comes from scale. A bot IP that hit a dozen other sites before reaching yours is caught before it even gets to the honeypot. The higher the minimum severity you require, the fewer false positives, but the slower the protection against newly active IPs.
For the messages that pass all five layers, there is an optional further step.
Most spam is stopped by the cheap server-side layers: honeypot, time trap, rate limit, content heuristics, and blocklist. But some borderline submissions make it through. The message was composed slowly, from a fresh IP, with no obvious keyword hits, but something about it still reads as automated or off-topic.
For these borderline cases, you can optionally pass the message text to a language model with a short spam/ham classification prompt. The model reads the actual content and returns a verdict: deliver to inbox, or quarantine for review.
A few practical notes on this approach:
This is a forward-looking option for sites that receive a lot of sophisticated spam. For most contact forms, the five server-side layers alone are sufficient.
Run the cheapest checks first and log everything while you tune.
The order of checks matters. Put the ones with the lowest processing cost first, so that the majority of spam never reaches the more expensive steps:
While tuning your thresholds, log rejected submissions with the reason. You cannot spot false positives in data you are throwing away.
The keyword list and time threshold that work for one site may be wrong for another. Review your logs, add terms that keep appearing, and drop ones that are not showing up.
Bot tactics shift over time. A keyword list that cleared your inbox in January may be stale by June. A quick monthly review is enough.
If you build on PageMotor, these layers are already built in.
EP Email ships a Spam Protection settings group that implements all five layers described in this guide. Here is what each setting does:
| Setting | What it does |
|---|---|
| Enable Honeypot | Adds a hidden trap field to every contact form. Any submission that fills it in is rejected. On by default. |
| Enable Time Trap | Rejects submissions that arrive faster than a real person could fill in the form. Catches bots that bypass the honeypot. On by default. |
| Minimum Fill Time (seconds) | The threshold for the time trap. Default is 3 seconds. Raise to 5 for very short forms; lower to 2 only if real visitors report failures. |
| Enable Rate Limiting | Limits submissions from the same IP address. On by default. |
| Rate Limit (minutes) | The rolling window for rate limiting. Default is 5 minutes. |
| Maximum Links per Message | Rejects submissions whose combined fields contain more than this many http(s):// links. Default is 2. Set to -1 to disable. |
| Spam Keyword Blocklist | One phrase per line. Case-insensitive match against all text fields. Pre-filled with common starters (bitcoin, crypto, casino, seo services, guest post, backlink, link building, rank higher, viagra, cialis). Tune to the spam you actually receive. |
| Enable Central Blocklist | Pulls shared threat intelligence from the ElmsPark network: IPs caught by other installs are blocked on yours, and vice versa. Requires a per-site token. Off by default. |
EP Email also ships a Gibberish-Name Filter (not covered in the general guide above) that catches bot-generated random strings in name fields, such as WwmDtQvvtgDtOaUwucR, using checks for long consonant runs, low vowel ratio, and random capitalisation. Real names pass cleanly. It can auto-ban the submitting IP to the local blocklist when it triggers.
All settings are in EP Email’s admin panel under the Spam Protection group and the Central Blocklist group directly below it.
The things that trip people up, and the fix for each.
Lower the minimum fill time. Start at 2 seconds and work upwards until spam appears again. Also check whether your form is being auto-filled by a browser password manager or autocomplete extension, which can complete the form faster than a human typing. If autocomplete is the culprit, consider starting the timer only after the first keypress rather than on page load.
Check your logs to see which check is being passed. If the honeypot is being bypassed, the bot is reading the HTML carefully and skipping it: verify that the field is hidden by CSS and not by the hidden attribute. If content is getting through, add the offending terms to your keyword blocklist. If the same IP keeps appearing, add it to your local IP blocklist.
A 5-minute window means someone who submits and then immediately wants to send a follow-up will be blocked. This is usually the right trade-off for a contact form. If you need to allow quick corrections, raise the rate limit window and accept a small increase in spam risk, or add a note on your thank-you page explaining that corrections can be sent by email.
Review the blocklist and remove any term that is too broad. A term like link would block half of all real messages; a term like link building is specific enough to be safe. If a real enquiry is being blocked, the offending keyword is almost certainly in your blocklist: check the rejected-submissions log to see which rule fired.
The sender’s IP may have been reported by another site, possibly because it is a shared office IP or a VPN exit node. Lower the minimum severity setting so only IPs with strong corroboration are blocked, or manually remove the IP from your local copy of the list. Contact the blocklist provider to dispute the report if the false positive recurs.