Forming Forms stops spam in layers. The first layers are invisible and on by default. A CAPTCHA is free and ready when a form needs more.
| Layer | Where | Default |
|---|---|---|
| Honeypot field | Form block → Spam protection | On |
| Submission time-trap | Form block → Spam protection | On |
| Per-visitor rate limit | Forming → Settings → Spam protection | 20 per hour |
| Akismet check | Forming → Settings → Spam protection | Off |
| CAPTCHA (reCAPTCHA, hCaptcha, Turnstile) | Provider in Settings, then per form | Off |
Blocked submissions are counted per day, and the Spam blocked (30 days) total appears on the Forming → Dashboard.
The honeypot
The form renders an extra text field, positioned off-screen, hidden from assistive technology and skipped by the keyboard, so no visitor ever encounters it. Bots that fill in every input fill this one too, and any submission arriving with it filled is treated as spam.
Leave it on. The only cost is one hidden input.
The time-trap
The form records when it was rendered, signed so it can’t be forged or left out. If a submission comes back in under two seconds, it’s rejected. A human hasn’t even read the labels in that time.
To tighten it:
add_filter( 'forming_min_submit_seconds', function () {
return 5;
} );
Raise it carefully. Someone who already knows what to type into a one-field form can be quicker than you’d expect.
What a caught submission sees
A submission flagged by the honeypot, time-trap, Akismet or a low reCAPTCHA v3 score gets the ordinary success response, the same thank-you message or redirect a real visitor gets. It just isn’t stored or emailed.
Bots learn nothing from that. An error message would tell an attacker which check tripped. The trade-off is that a false positive looks like a success to the person who hit it, which is the main reason not to set the time-trap aggressively.
The rate limit and the checkbox-style CAPTCHAs are different: a real person can trip them, so they show a message.
Rate limiting
Each visitor (by IP address) can send a limited number of submissions per hour across your forms. Set Submissions allowed per visitor per hour under Forming → Settings → Spam protection. The default is 20, and 0 turns it off. Over the limit, visitors are told to try again later.
Behind a proxy or CDN such as Cloudflare, every visitor may appear to have the same IP. Use the forming_client_ip filter to read the real address from a header your proxy sets. See the Developer Reference.
Akismet
If the Akismet plugin is active and connected, turn on Check submissions with Akismet. Each submission’s name, email, website, message, IP address, browser and referring page are sent to Akismet. Submissions Akismet calls spam are silently dropped.
CAPTCHA
All three providers are free in Forming Forms.
1. Get your keys
| Provider | Where to get keys | Modes |
|---|---|---|
| Google reCAPTCHA | google.com/recaptcha/admin | v2 “I’m not a robot” checkbox, v2 Invisible, v3 score-based (no challenge) |
| hCaptcha | dashboard.hcaptcha.com | Checkbox, or Invisible (challenge only when needed) |
| Cloudflare Turnstile | Cloudflare dashboard → Turnstile | Managed, non-interactive or invisible, set on the site key in Cloudflare |
For reCAPTCHA, create keys for the same type you’ll choose in Forming. v2 keys don’t work with v3, and v3 keys don’t work with v2.
2. Choose the provider
Go to Forming → Settings → Spam protection, choose a CAPTCHA provider and paste the site key and secret key. Then:
- reCAPTCHA: pick the reCAPTCHA type. For v3, set the Minimum score (0.5 suits most sites. Raise it if spam gets through).
- hCaptcha: pick the hCaptcha widget: Checkbox or Invisible.
Click Save settings.
3. Turn it on per form
Select the Form block, open Spam protection and switch on CAPTCHA. Only forms with it on show a CAPTCHA, and the provider’s script loads only on pages with such a form. All other pages stay free of third-party scripts.
Verification happens on your server. If the CAPTCHA is missing or fails, the visitor sees a message asking them to complete it.
Privacy: CAPTCHA providers collect information about the visitor’s device and behavior. Mention the provider in your privacy policy. Its terms and privacy links are listed in the plugin’s readme under External services.
If spam still gets through
- Confirm the toggles are on for that form. They’re per-form, and a form copied from elsewhere may have them off.
- Lower the rate limit, for example to 5 an hour.
- Turn on Akismet if you already use it for comments.
- Turn on a CAPTCHA for the form. Turnstile and invisible hCaptcha add the least friction.
- Raise the time-trap with the filter above.
- Filter at the server: Cloudflare’s bot protection or your host’s firewall stops traffic before PHP runs.
Your own spam rules
Add a check with the forming_spam_check filter. Return a WP_Error to reject the submission silently, or add 'silent' => false to show your message:
add_filter( 'forming_spam_check', function ( $result, $submission ) {
if ( is_wp_error( $result ) ) {
return $result;
}
// Spam checks run before validation, so read the raw request.
$fields = (array) $submission->request->get_param( 'fields' );
$message = isset( $fields['message'] ) ? (string) $fields['message'] : '';
if ( false !== stripos( $message, 'crypto' ) ) {
return new WP_Error( 'my_keyword', 'Spam keyword.' );
}
return $result;
}, 30, 2 );