WPForms and Forminator Spam Protection Without a Captcha
WPForms and Forminator spam protection without a captcha means the site checks that a real browser rendered and submitted the form, instead of asking the sender to prove it. ReportedIP Hive does that for both plugins: a bot’s submission is refused as the form’s own error above the form, no entry is written, no mail goes out, and a genuine sender never sees a puzzle, an image or an extra step.
Both adapters are part of the Business plan and switch on with one toggle each on the Protection page under Form Protection. WPForms arrived with Hive 2.1.65, Forminator with 2.1.66; they are tested against WPForms Lite 2.0 and Forminator 1.57.
Why a captcha is the wrong tool for a contact form
A captcha asks the wrong person to do the work. The sender, who wants to reach you, reads distorted letters or clicks on traffic lights; the script that floods the form was never going to solve it and moves on to a site with a weaker one. Every failed attempt costs you a message, and you never learn how many gave up.
Bots come in two groups, and a form has to handle both. The first fills in every field it finds, including the one it should skip. The second never loads the page: it posts straight at the endpoint with a payload copied from one real submission. WPForms and Forminator each ship a honeypot field for the first group. The second walks past a honeypot, because it never renders the markup that carries it.
How the execution proof judges a WPForms or Forminator submission
Hive plants one invisible anchor field in every form of both plugins, excluded from the tab order and from screen readers. A small script adds a second field whose name is different on every installation, so a payload recorded on one site fits no other. On submission the server reads both fields back and sorts the request into one of four verdicts:
| Verdict | What the submission carried | What happens |
|---|---|---|
proved | Anchor empty, proof field present | A browser rendered the form and ran its script. The entry is saved. |
tripped | The anchor field, filled in | Refused. A filled invisible field is automation with no innocent explanation. The address is blocked and reported on the first try. |
failed | Anchor empty, proof field missing, the site knows it renders the field | Refused. The page was fetched, the script never ran. The address is not counted. |
absent | Neither field present | Lenient. Hive has not yet seen itself render the field on this site, so nothing is judged. |
Nothing request-specific reaches the HTML. The anchor is the same on every page load and the randomised name lives in the script, so a page cache can keep serving the form for as long as it likes without a single visitor being locked out. The rule behind the four verdicts is one method shared by every protected form, so WPForms, Forminator, the comment form and the WordPress sign-up read the same evidence the same way.
WPForms: the anchor sits in front of the submit button
WPForms fires wpforms_display_submit_before inside the form element right before the submit button, on the page-load path and the background path alike, and only when the plugin renders a form of its own. Hive writes the anchor there. The confirmation message is rendered elsewhere, so the anchor never lands next to a thank-you text.
The verdict is delivered on wpforms_process, which fires after the plugin has validated every field and before it writes an entry or sends a mail. A refusal placed in the processor’s error list at that point stops both. WPForms renders that list above the form as its own header error, with a page reload and in the background response alike, so the sender always reads why nothing was sent. Field errors come first by construction: the hook is not reached while any field is invalid, so an incomplete form costs no lookup.
The browser side needs no hook of its own. WPForms validates on the form’s submit event, which is the one Hive’s script already listens to, so the proof field is filled and the held submit works unchanged. One thing had to be forced: the plugin’s stylesheet resets hidden elements, which had put the decoy on the page for a visitor to fill in. The off-screen rule of the decoy field now wins against that reset.
Forminator: the anchor sits in the submit block, the verdict in the error list
Forminator builds the block that carries its nonce and the submit button through forminator_render_form_submit_markup, and that block is always written inside the form element, on the page-load render and on the AJAX render alike. Forminator hangs its own honeypot there for the same reason, and Hive appends its anchor to it. The wider forminator_render_form_markup filter would be wrong: its markup reaches past the closing form tag, so an appended field would land outside the form.
The verdict lands on forminator_custom_form_submit_errors, the second step of the submission handler, before the entry object is built, before it is saved and long before the mail is sent. A non-empty error list there stops the handler, and Forminator answers with the list the sender reads at the form. Nothing is stored, nothing is sent, no add-on runs. Forminator keys every error by a field id and renders the message next to that field, so the refusal is attached to the first field of the submission. The hook fires a second time while attachments are handled; a refusal already in the list is never added twice.
A form fetched after the page is judged like one in the page
A Forminator form can be loaded into the page afterwards, and both plugins render a form again after a failed validation. The server side is covered because the anchor rides inside the plugin’s own render, whichever path produced it. The browser side is covered at submission time: Hive’s script acts on the submit event, and if no computed answer is in stock yet, it holds the submit for at most eight seconds while it fetches one, then sends the form on exactly as the visitor sent it. A visitor who clicks before the task has come back is not a bot, and an empty field would have refused them.
Why a refused submission is an error and not a spam-folder entry
Both plugins offer a spam folder. WPForms can mark an entry as spam, and Forminator’s own spam filter drops a submission into the spam folder and, depending on a per-form setting, shows the sender a success message. Hive uses neither, on purpose. An entry in a spam folder is a message the sender was thanked for and the operator never reads. If the verdict was wrong, the person who wrote to you believes their message arrived and waits for an answer that never comes.
A refusal from Hive is the form’s own error instead: the header error WPForms uses for its own “form has not been submitted”, the field error list Forminator uses for a required field left empty. The sender sees the message at the form, can read why, and can try again. No entry is written, no notification goes out, and no confirmation is shown. This plugin never loses a message without at least telling the sender so.
What a bot sees and what a visitor sees
| Genuine visitor | Script posting at the endpoint | Bot filling every field | |
|---|---|---|---|
| Loads the page | Yes | No | Yes |
| Runs the script | Yes | No | Usually not |
| Proof field | Present | Missing | Missing or wrong |
| Anchor field | Empty | Empty or missing | Filled |
| Extra step for the human | None | ||
| Result | Entry saved | Refused at the form | Refused, address blocked and reported |
What the check cannot do is tell a person from a botnet driving a real browser. It tells a browser from a script. For the address behind the browser, Hive runs a second, independent check: the community threat check refuses a submission from an address the site would refuse a sign-in to, at the protection level the sign-in page enforces, and the sender is told why. That check needs Community Network mode; the execution proof works in Local Shield mode as well, with nothing leaving the site.
What a filled decoy costs the address
Filling a field nobody can see takes a machine, so Hive does not wait for a second and a third attempt before acting. A submission with a filled anchor goes straight to the block ladder and to the community report, the way the comment filter has treated the same evidence since 2.1.52; the report names the form it came through. An address on your whitelist is exempt, because the lookup sits in the dispatcher every sensor ends up in, not in the counter a fast sensor skips.
A visitor whose browser simply never ran the script is untouched by this. Their submission is refused with a message that says why, and their address is never counted, because a missing proof is not evidence of a machine.
Switching it on in four steps
- Update to Hive 2.1.66 or later on a Business plan. On a lower plan the WPForms and Forminator switches stay visible and locked, with the plan they need written next to them.
- Turn on the WPForms or Forminator switch on the Protection page under Form Protection. Switching it on clears the common page caches, and for 24 hours a submission that never carried the proof is still accepted, so a page cached without the field cannot lock anybody out. The
reportedip_hive_form_adapters_gracefilter buys more room on a site whose cache outlives a day. - Run the self-test under Tools → Diagnostics. The card lists what is switched on and then runs three passes with the browser you are sitting in front of: like a visitor, like the same visitor twice, and like a bot. No real form is submitted, no mail goes out and no entry is stored.
- Optionally start in report-only mode, which logs every verdict and refuses nothing. A week of logs shows what would have been refused before anything is.
The computation check, included from Professional and therefore on Business, closes the one shortcut the plain proof field leaves open: reading the field name out of the page and posting it back. The browser fetches a small task from your own site and works it out in the background; the task is signed with the site salt, good for ten minutes and accepted once, and the sender still notices nothing. It needs HTTPS; without it the task keeps its signature, expiry and single use but carries no arithmetic.
Which form plugins get the same protection
| Form | Plan | Tested against |
|---|---|---|
| Comment, sign-up and lost-password forms | Free | WordPress core, WooCommerce, Multisite |
| Your own forms, through the form API | Free | Three calls: field, check, passes |
| Contact Form 7 | Every plan | The version published on wordpress.org |
| WPForms and WPForms Lite | Business | WPForms Lite 2.0, page-load and background path |
| Forminator | Business | 1.57, forms in the page and forms loaded afterwards |
| Gravity Forms | Business | 3.1, including multi-page and background forms |
| Formidable Forms and Formidable Forms PRO | Business | 6.35 |
| Elementor Forms | Business | Elementor PRO 3.34, the form widget exists only there |
| Ultimate Member | Business | 2.13, sign-in, sign-up and password forms |
Each plugin has its own switch. The whole layer, the master switch, a second switch that leaves sign-up and password reset out, and report-only mode sit together on the Protection page. REPORTEDIP_HIVE_DISABLE_FORM_PROOF in wp-config.php switches the layer off for the case where a visitor cannot submit and you need the site working before you debug.
Frequently asked
Do WPForms or Forminator still need a captcha with Hive switched on?
Not against scripts and bots. The execution proof catches a submission that never rendered the form and a bot that fills the invisible field, and the community threat check refuses an address the network already knows. A captcha adds nothing to that except a step the sender has to take. What no field can do is tell a person from a botnet driving a real browser, and a captcha cannot do that either.
Does it work next to Akismet, the WPForms anti-spam token or the Forminator honeypot?
Yes. Hive judges the submission on the plugin’s own processing or validation hook, before an entry exists. A refused submission never reaches the entries list, the spam folder or any other anti-spam check. Everything that passes goes on to the checks you already run.
What happens to a visitor with JavaScript switched off?
On the form plugins a submission without the proof is refused with a message that says why, because on a contact form there is no moderation queue to fall back on. The address is not counted and not blocked. A site with many such visitors can run the layer in report-only mode, or leave the WPForms and Forminator switches off and keep the free forms covered.
Does the check slow the form down or break page caching?
No. The anchor is identical on every page load and nothing request-specific reaches the HTML, so a cached page stays valid. The script fills one field at submission time and measures how long the form was on screen; the starting point comes from the browser, never from the markup, so a cached page cannot carry a stale one. The computation task travels in a POST that no cache stores.
Is it GDPR compliant?
The proof collects nothing about the visitor beyond whether two hidden fields were present on submission and how many seconds the form was on screen. No fingerprinting, no third-party request, no image served from another domain. In Local Shield mode nothing leaves the site at all. The WordPress plugin documentation carries the full data-handling summary.
Related reading
- Gravity Forms without a captcha, the adapter for a plugin that submits its forms itself
- Form honeypot and execution proof, the four verdicts and how the comment form scores them
- Hive 2.1.66 release notes, the one rule every form now shares
See the WordPress plugin documentation for the full settings reference and the plan comparison for what Business includes. Explore ReportedIP Hive →