Spam filtering
How a submission is checked before it reaches your inbox, what each layer does with a match, and which ones you can tune.
The layers, in order
Every submission passes through the same checks, cheapest first. Most of them answer a bot with an ordinary success response, so it never learns which rule caught it. You see the result on the form's Spam tab.
| Layer | Default | What happens to a match |
|---|---|---|
| Rate limit | Always on | Refused with 429. See Rate limits. |
| Honeypot | On | Success response, nothing stored, not billed. |
| Your block rules | None | Success response, nothing stored, not billed. |
| Disposable email | Off | Success response, nothing stored, not billed. |
| Turnstile challenge | Off | Refused with challenge_failed (403). |
| Content scoring | Standard | Near-certain spam: success response, nothing stored. Borderline: stored in the Spam tab. |
Refused attempts never become submissions, so they cannot fill your inbox or use your quota. The top of the Spam tab counts them over the last 30 days, by reason, so an empty list of diverted messages does not mean nothing is happening.
Honeypot
A hidden text input that people never see and form-filling bots fill anyway. It is on for every new form. Add it to your markup and it starts working:
<input
type="text"
name="_mtf_honeypot"
tabindex="-1"
autocomplete="off"
aria-hidden="true"
style="position:absolute;left:-9999px"
>Hide it off-screen as above. Do not use display:none or type="hidden", which many bots skip, and never mark it required. If you are moving from another service, the names _gotcha, _honeypot and _honey are treated as the honeypot too, so existing markup keeps working without an edit.
201 body. If a test submission never shows up, check that your browser's autofill has not put a value in the hidden field.Your own block rules
Under Block list on the Spam tab, add rules for things you already know you never want. A rule is an instruction, not a signal: a match is refused outright, whatever the score would have said. There are 5 kinds:
| Kind | How it matches |
|---|---|
| Word or phrase | Blocks a submission if this appears anywhere in its text. Case-insensitive, and matched inside longer words. |
| Email domain | Blocks any email field ending in this domain. Subdomains included, so blocking example.com also blocks mail.example.com. |
| IP address | Blocks a single exact IP address. |
| IP range | Blocks a whole IPv4 range in CIDR notation, such as 203.0.113.0/24. One rule instead of the 256 an exact-address rule would need. |
| Country | Blocks every submission the edge places in this country, by two-letter code. Nothing is blocked when the edge cannot say where a request came from. |
Each rule shows how many submissions it has matched and when it last did, so a rule that never fires is easy to spot. Each diverted message on the Spam tab also offers a Block action for its email domain or country.
Separately, you can refuse addresses from 69 known throwaway email providers. It is off by default, because a support desk or bug tracker has good reasons to accept one. Turn it on under Protection, then Adjust.
Turnstile challenge
For a form under sustained attack, turn on Cloudflare Turnstile. It is verified on our side before the submission is accepted, and it fails closed: if Cloudflare cannot be reached, the submission is refused rather than let through.
- Create a Turnstile site in your own Cloudflare account and add every domain the form is embedded on. The keys are yours because Cloudflare ties a site key to hostnames.
- Paste both keys into the Cloudflare Turnstile panel on the Spam tab. The secret is stored encrypted.
- Put the widget snippet the panel shows inside your form. It writes the token to
_mtf_turnstile_token; Cloudflare's defaultcf-turnstile-responseworks too. - Turn the challenge on only after the widget is live on every page.
challenge_failed. Server-side calls made with an API key skip the challenge.Content scoring and sensitivity
Every submission that gets this far is scored from 0 to 100 on signals such as link count, known spam phrases and repeated values. Two thresholds decide what happens, and you pick which pair applies under Protection, then Adjust:
| Sensitivity | Spam tab from | Refused from | What you will see |
|---|---|---|---|
| Lenient | 90 | never | Nothing is ever refused outright, and only near-certain spam is diverted. For forms that legitimately receive links, code, or long technical messages. |
| Standard | 80 | 95 | The default. Obvious bulk spam is refused, borderline messages go to the Spam tab for you to check. |
| Strict | 55 | 85 | Diverts anything with several links, sales phrasing, or the shape of a template. Expect to check the Spam tab, and to find the occasional real message in it. |
A submission in the review band is stored with status spam. It sends no email, fires no webhook, Slack message or autoresponder, and waits on the Spam tab. Restoring one moves it to the inbox.
What counts against your quota
- Anything refused (rate limit, honeypot, block rule, disposable email, challenge, or a score past the refuse threshold) is never counted.
- A submission diverted to the Spam tab is stored so you can review it, but it is not counted. It still needs room under your limit to be stored, and restoring it to the inbox does not charge it later.
- If an ordinary inbox submission turns out to be spam, Mark spam hands its unit back, once. Toggling it back and forth does not refund it again.