Spam form submissions are usually a nuisance. Repeat hateful or abusive submissions on the same form are a different problem — the person isn't a bot trying to slip past a filter, they're a specific individual trying to get the same message through, on purpose, more than once. That distinction is why Formstack's built-in tools don't actually solve it, and why this had to be built as a separate plugin.
The Problem: A Human, Not a Bot
Most spam protection is built to answer one question: is this submission coming from a machine? Formstack's own defenses follow that logic. Invisible reCAPTCHA runs in the background on every form, using risk analysis to separate humans from bots before a submission goes through. It's a solid layer of protection, and it works well against the kind of spam it's designed for — automated form-flooding, scraped-list blasts, bulk junk.
It does nothing for a real person, filling out the form themselves, submitting content that's derogatory or hateful on purpose. reCAPTCHA isn't evaluating what someone wrote — it's evaluating whether they're human. In this case, they are. The submission sails through every bot check Formstack has, because it isn't bot behavior. It's targeted, intentional, human behavior, and that's a different problem that needs a different kind of check.
Where Formstack's Built-In Duplicate Protection Falls Short
Formstack does offer something for repeat submissions: a "unique" setting you can apply to a field, most commonly email or phone number. Once that's turned on, Formstack rejects any submission that repeats a value already on file for that field. On paper, that sounds like exactly what's needed here.
In practice, it's too blunt to use. The unique-field setting doesn't distinguish between a bad-faith repeat submission and a legitimate one — it just blocks any second submission with the same value, for anyone. Formstack's own documentation acknowledges this tension directly: on forms where the same answer might legitimately show up twice, like a satisfaction survey, "unique" isn't a workable setting at all, because the platform has no way to tell an intended duplicate from an unintended one. There's no content awareness built into it, no way to say "block this email only because of what came attached to it last time," and no keyword layer at all. It's a single on/off switch tied to one field, not a moderation system.
That gap is really the core issue: Formstack was built to collect form data reliably, not to moderate the content of what's submitted or manage a list of specific people you want blocked. There's no native way to maintain an editable list of offending email addresses. There's no native way to flag language. And there's no way, once someone is identified as a problem, to actually restrict what they can do on the rest of the site — Formstack's control ends at the form. Everything past "should this specific submission be accepted" is outside what the platform is built to handle, which is exactly the space this plugin needed to fill.
The Plan: Two Checks, Working Together
With the gap defined, the plugin splits the solution into two checks that run at different points and solve different halves of the problem.
Content Inspection Before Submission
Before a Formstack submission goes through, the plugin checks it against two managed lists:
- Email address — matched against a list of known offending emails, editable by the site owner
- Keywords — derogatory or hateful language flagged for blocking, also editable by the site owner
If either check matches, the submission is blocked before it reaches Formstack. Nothing gets recorded, nothing gets sent.
The part that matters more than the block itself is what happens next: the plugin sets a cookie on the visitor's browser that restricts them to the homepage. Every other page redirects back to it. This isn't a "your message couldn't be sent" error — it's a soft lockout of the rest of the site, which is the piece Formstack has no mechanism for at all.
If the visitor clears their cookies and tries again, the same check runs on the next submission. If it matches, the cookie is simply set again — the block reapplies automatically, no matter how many times they clear cookies, as long as they keep using the same flagged email or language.
Cookie-based blocking does have a real limit: it lives entirely in the visitor's browser. A private/incognito window starts clean, and a different browser or device bypasses it outright. For the content-based lockout, that's an acceptable tradeoff, since the check re-runs and re-blocks every time regardless. For stopping repeat submissions specifically, it's not reliable enough on its own — which is exactly the gap Formstack's own "unique field" fell into as well, just from a different angle.
Duplicate Detection Against Formstack's Own Data
For repeat submissions, the plugin uses Formstack's API to cross-reference the submission itself against Formstack's submission history — rather than anything tracked in the visitor's browser. Before a submission is accepted, the plugin compares the submitted email address and/or physical address against previous submissions within a defined time window. If a match turns up, the duplicate is prevented. If nothing matches, the form submits normally.
Because this check runs against data stored in Formstack rather than the visitor's device, incognito mode, clearing cookies, and switching browsers all stop mattering. The only thing that changes the outcome is submitting genuinely different information — which, for someone specifically trying to repeat the same submission, defeats the point of trying to get around it.
One Plugin, Filling the Gap Formstack Leaves Open
Both checks live in the same plugin, and each one covers ground Formstack's native tools don't reach. The content check handles blocking specific people and specific language, with lists the site owner can actually manage — something Formstack has no equivalent for. The API-based duplicate check handles the repetition problem using data that can't be cleared or bypassed from the visitor's side, which is a more reliable version of what the unique-field setting was trying, and failing, to do.
Neither layer depends on the other, but together they cover what Formstack was never built to handle on its own: not just "is this a bot," but "is this a specific person doing a specific thing we don't want happening again."

