Pre-filters
Before any API call, Formsieve runs eight free local checks, cheapest first. The first one that reaches a decision ends the check; the others only compute signals. Each can be switched off under Settings → Detection → Free local checks (Settings reference), and every decision is logged with its reason code (Reason codes). They run only once Formsieve is set up (route, key and consent) or Test mode is on; before that nothing is checked.
Before the first check, every field value longer than 20,000 characters is cut to that length, so an enormous post cannot slow your site down. The model is still told the full length of the message, and the cut adds a local hint (Heuristics). Contact Form 7 checks field lengths before any spam filter runs: by default a text area takes at most 2,000 characters and a one-line field 400 (the maxlength: option of a form tag changes this), and a longer submission is refused (by default with "This field has a too long input.") before Formsieve sees it. The 20,000-character cut therefore only matters on forms that raise those limits.
| Order | Check | Default | Decides |
|---|---|---|---|
| 1 | Allowlist | on | allow |
| 2 | Honeypot | on | block |
| 3 | Time-to-submit | on, 3 seconds | block |
| 4 | Rate limit | on, 5 per 10 minutes per IP | block |
| 5 | Blocklists | on | block |
| 6 | Trivial content | on | allow |
| 7 | Replay cache | on, 7 days | reuse the earlier verdict; block bulk duplicates |
| 8 | Heuristics | on (aggressive mode off) | local hints only; block in aggressive mode |
The rate limit and the blocklists run before the two checks that can allow without asking the model (trivial content, and a cached allow in the replay cache), so an explicit block or a flood from one address is never overridden by an earlier verdict.
1. Allowlist
Allows without a check: trusted logged-in users (setting Trusted users), the e-mail addresses, domains and IP ranges on your Always allow lists, and senders you have marked "not spam" before (learned trusted senders, stored as hashes). Allow lists win over every other check.
The e-mail and domain entries, learned trusted senders included, match the address typed into the form, which anyone can type. Never put your own site's address or domain on them, and keep the domain list to partners whose addresses outsiders are unlikely to guess. An IP entry trusts whatever address the form plugin reports (see Rate limit).
- Gravity Forms: a trusted user is a logged-in user who can edit Gravity Forms entries or forms (Administrators, and roles you gave those Gravity Forms capabilities). Authors and contributors are checked like visitors.
- Contact Form 7: a trusted user is a logged-in user who can edit that contact form (Contact Form 7's
wpcf7_edit_contact_formcapability, which maps topublish_pages: Editors and Administrators by default).
2. Honeypot
A hidden field that people never see and bots tend to fill in.
- Contact Form 7: Formsieve adds its own hidden field,
_fsv_hp, to each form it checks. A filled-in value blocks the submission (honeypot). - Gravity Forms: use the form's own anti-spam honeypot, switched on in the form's settings (called "Advanced Honeypot" from Gravity Forms 3.1). It runs before Formsieve, and a submission it flags never costs an API call. Formsieve adds no honeypot field of its own to Gravity Forms, so its settings show no Honeypot switch there.
3. Time-to-submit
Formsieve puts a signed token with the page-load time into each form. A submission sent faster than the minimum (3 seconds by default) is blocked (timing). The token is signed on your server, so the visitor's clock does not matter. A missing or forged token does not block; the time is simply not sent to the model. With full-page caching the token carries the time the page was cached, which can only make a submission look slower, never too fast. On Gravity Forms a form shown again after a validation error, or the next page of a multi-page form, keeps the token of the first page load, and a form resumed with Save and Continue counts as already old enough.
4. Rate limit
More than 5 submissions from one IP address within 10 minutes (both adjustable) are blocked (rate_limit). An IPv6 address is counted with its whole /64 network, because one IPv6 subscriber usually controls all of it. IP addresses are hashed before they are counted and are never sent anywhere. Formsieve uses the object cache when your site has one. While Rate limit is off, Formsieve stores nothing for it and the model is not told how many submissions came from the address in the last hour.
Behind a proxy, CDN or tunnel. Formsieve uses the visitor address that Gravity Forms or Contact Form 7 records, which is the server's REMOTE_ADDR. If every request reaches WordPress from your proxy, all visitors share one address and the rate limit blocks real people after a few submissions. Fix the address on the server (for example the proxy's real-IP module), or pass the forwarded address with the gform_ip_address or wpcf7_remote_ip_addr filter, trusting only a header that your own proxy sets; otherwise switch the rate limit off. The IP allow and block lists and the bulk-duplicate check use the same address.
On a Gravity Forms form whose Personal Data settings prevent storing IP addresses, Formsieve does not use the IP either: the rate limit, the IP allow and block lists and the bulk-duplicate check (same text from several addresses) do not apply to that form. The form's Formsieve settings show a notice about it.
5. Blocklists
Blocks (blocklist:<kind>), in this order:
- senders you have marked "spam" before (learned, stored as hashes; free e-mail domains are never blocked as a whole);
- your Always block e-mail addresses, domains, IP addresses and ranges, and words or phrases. A blocked domain matches the sender's e-mail domain and the domain of any link in the message;
- WordPress's Settings → Discussion → Disallowed Comment Keys, matched as case-insensitive text in the submission (switch off with Disallowed comment keys);
- disposable e-mail domains, from the bundled list (switch off with Disposable email).
The Blocklists switch covers all four: while it is off, learned blocked senders, Disallowed Comment Keys and disposable domains are not checked either, even though their own switches still show as on.
6. Trivial content
A submission with no free text, for example only a name, an e-mail address and a ticked box, is allowed without an API call (trivial): there is nothing for the model to judge. It is not trivial when any field contains a link or the name field is unusually long, because spam tools put their pitch in the name field of short forms.
7. Replay cache
Formsieve remembers a keyed hash (with your site's secret salt) of each checked text (normalised), together with the question-set version and model, for 7 days. The same text again reuses the earlier verdict for free (replay); an administrator's Re-check never does. The same text from 3 or more different IP addresses (IPv6 counted per /64 network) is blocked as a bulk duplicate (bulk_duplicate). Only hashes and numbers are stored, in WordPress transients.
8. Heuristics
Local hints computed on your server and never sent: URL shorteners and BBCode links, share of capital letters, long consonant runs, a field value equal to its label, identical values in several fields, crypto-wallet addresses, a message that is only a phone number, a field longer than 20,000 characters, and prompt-injection patterns (known "ignore previous instructions" phrasing, deliberately misspelled variants, words joined by hyphens, dots or invisible characters, reversed text, long base64 blocks, and zero-width characters, which are stripped). Each field is screened for injection on its own, so words from two fields never join into a phrase nobody wrote. They drive aggressive mode and the injection check below. With Heuristics off none of them is computed, so aggressive mode cannot block and only Jev's own answer can trigger the injection rule.
What the model receives as signals is fixed, whatever this switch is set to: the number of links and their domains, whether a phone number is present, the time to submit, submissions from the same IP address in the last hour (a count, sent only while the rate limit is on), the writing systems used, the message length (the length the visitor submitted, even when a field was cut), and whether you marked the sender spam or not spam before.
Aggressive mode (off by default) blocks without an API call when two or more strong hints occur (heuristics:aggressive): text addressed to the filter, BBCode links, crypto addresses, link shorteners, a value equal to its label, identical values in every field, a field longer than 20,000 characters. It is meant for sites that often reach their cost limits; it is blunter than the model.
A prompt-injection match also forces the model's probability to at least 0.90 (see How the probability is decided).