Why no CAPTCHA puzzles
CAPTCHAs trade spam reduction for friction: they slow down or block real visitors, they break for screen readers and other assistive tech, and increasingly they don’t even stop bots - modern solving services defeat most of them cheaply. They also block the exact audience Formpaste is built for: an AI agent legitimately submitting a form on a human’s behalf shouldn’t have to solve a puzzle designed to detect it as non-human. Instead, every submission is scored by cheap, inline heuristics - no external API call, no per-submission cost, no visible challenge. See Spam filtering for the full mechanics; this guide is about using them well.1. Wire up the honeypot (botcheck)
The single highest-value thing you can do is include the botcheck field and hide it
properly:
- Hide it with CSS (
display:none), nottype="hidden". Unsophisticated bots that specifically skiptype="hidden"inputs will still fill in atextinput that’s merely styled invisible - that’s what catches them. - Don’t remove it “because nothing fills it in.” An empty
botcheckfield submitting successfully alongside real data is exactly what should happen for a human visitor — it’s silent and free. It only matters when something else - a bot, or a browser extension autofilling every field it can find - populates it.
botcheck wired in; if you hand-rolled your form instead of copying one, add it.
2. Add the timing field (_ts)
An optional _ts field carries the timestamp your form was rendered. A submission that
arrives implausibly fast - faster than a human could plausibly have read the form and
typed a response - scores as suspicious. This is a cheap way to catch scripted
submissions that skip rendering entirely and just POST a payload.
Date.now() in
JavaScript, or the server-render time for a static page).
3. Let the rest run automatically
Onceaccess_key is wired, the remaining heuristics need no setup and run on every
submission:
- Link density - a message packed with multiple URLs is scored as likely spam.
- Rate limiting - repeated submissions from the same source in a short window are throttled, independent of content.
- Disposable / duplicate email detection - submissions from disposable-email
providers, and near-identical repeat submissions, are flagged. If your use case
legitimately expects disposable addresses, turn on
allow_disposable_emailfor that form under Domain allowlist rather than fighting the default.
4. Lock down the domain allowlist
access_key is designed to sit in public page source - it isn’t a secret, and CORS on
/submit is intentionally permissive so any origin can POST. The allowed domains
list on each form is the actual boundary that stops someone from copying your key and
routing unrelated traffic (including spam) into your inbox. An empty allowlist accepts
any origin - fine while you’re setting up, but worth locking to your real domain(s)
once the form is live. See Domain allowlist.
5. Treat quarantine as a review queue, not a black hole
Nothing that reaches a validaccess_key is silently dropped. Anything that scores as
suspicious lands in the form’s Spam tab in the
dashboard instead of your inbox - check it
periodically for false positives. On Free, quarantine is read-only, so a legitimate
submission that was misflagged needs the visitor to resubmit once you’ve fixed whatever
tripped the heuristic (usually a botcheck wiring issue or an overly strict
disposable-email setting). Pro adds a one-click “not
spam” rescue that re-delivers it directly.
What this doesn’t do
Heuristic scoring reduces spam volume; it doesn’t guarantee zero. A determined, well-funded attacker who renders your form in a real headless browser, waits a plausible amount of time, and sends one submission at a time from a residential IP can still get through - the same way they could defeat a CAPTCHA. The goal is raising the cost of spamming a form well above the cost of the alternative (a real inbox with no filtering at all), not building an impenetrable wall.Spam filtering
What’s checked and how scoring works.
Quarantine
Where flagged submissions go, and how to review them.
Domain allowlist
The real security boundary behind a public access_key.
Error reference
Including
rate_limited and other spam-adjacent codes.