Onsite Spam Guard

Description

Onsite Spam Guard blocks spam on the forms your visitors actually use — WordPress comments, WooCommerce product reviews, Jetpack contact form blocks, and WP Job Manager job submissions — without sending anything to a third-party service, requiring an API key, or putting a CAPTCHA in front of your users.

Protection is built from a pipeline of independent guards. Each guard is a small, focused check (a hidden honeypot field, a submit-speed gate, a keyword filter, and so on). Guards run in priority order, and the first one to fail blocks the submission. Every guard can be toggled and tuned from a single settings page, and every block can be logged for review.

Spam guards

  • Honeypot — a hidden field that bots fill in but humans never see.
  • Duplicate detection — rejects identical submissions sent within a short window.
  • Time gate — rejects submissions completed faster than a human could plausibly type.
  • Signature — requires a server-signed token proving the form was served by this site, deterring automated cross-site posting.
  • Rate limit (optional) — throttles repeated submissions from the same sender within a short window, keyed on the logged-in user when there is one and the connection IP otherwise.
  • Link limit — flags submissions that contain too many URLs.
  • Keyword block — rejects submissions matching a configurable blocklist of words or phrases.
  • Behavioral analysis (optional) — scores mouse movement, clicks, and time on page to spot bot-like interaction.

Why you might choose it

  • No external services. Nothing leaves your site. No accounts, no API keys, no per-submission fees.
  • No CAPTCHA. Protection is invisible to legitimate visitors.
  • Allowlist. Trusted IPs, CIDR ranges, email addresses, and email domains bypass every guard.
  • Logging with retention. Blocked submissions are recorded in a dedicated table with a paginated admin viewer, and old entries are pruned automatically on a schedule you control.
  • Privacy-aware. The plugin registers suggested privacy-policy text describing exactly what it records.
  • Modern, dependency-free code. PHP 8.2+, vanilla front-end JavaScript (no jQuery), and no runtime third-party libraries.

Works with

  • WordPress comments (always).
  • WooCommerce product reviews (when WooCommerce is active).
  • Jetpack contact form blocks (when Jetpack is active).
  • WP Job Manager job submissions (when WP Job Manager is active).
  • Contact Form 7 forms (when Contact Form 7 is active).
  • BuddyPress private messages (when BuddyPress is active; off by default).

Screenshots

Installation

  1. Upload the onsite-spam-guard folder to /wp-content/plugins/, or install it through Plugins Add New.
  2. Activate the plugin through the Plugins menu in WordPress.
  3. Go to Spam Guard Settings to choose which form types to protect and to enable or tune individual guards.
  4. Review anything that gets blocked under Spam Guard Spam Logs.

No further configuration is required — sensible defaults are applied on activation.

FAQ

Does this send my data to any external service?

No. Every check runs on your own server. Nothing about a submission is sent anywhere outside your site.

Will legitimate visitors see a CAPTCHA or extra step?

No. All protection is invisible. The honeypot field is hidden, and the timing and behavioral checks happen in the background.

What does it store, and for how long?

When a submission is blocked (and logging is enabled), the plugin records the guard that blocked it, the form context, the reason, a short excerpt of the content, the visitor IP address, and the browser user-agent. Entries older than the retention window (default 30 days, configurable; set to 0 to keep them indefinitely) are pruned automatically. The plugin also registers suggested privacy-policy text you can add to your site’s policy.

I’m behind Cloudflare or a load balancer and the wrong IP is logged.

By default the plugin uses the direct connection IP, because forwarded headers can be spoofed to bypass the allowlist. If your site sits behind a trusted reverse proxy, enable Trust proxy headers for IP detection under Spam Guard Settings Allowlist.

A legitimate submission was blocked. What do I do?

By default a blocked comment or review is placed in the spam queue (Comments Spam) rather than being rejected outright, so you can restore a false positive with one click — nothing is lost. Open Spam Guard Spam Logs to see which guard blocked it and why, then loosen that guard on the settings page — for example, raise the link limit, lower the behavioral threshold, or add the sender to the allowlist. If you would rather reject blocked comments with an error message, enable that option under Spam Guard Settings General.

Can I limit how often one person can submit?

Yes. Enable Rate limit on the Guards tab, then set the maximum number of submissions and the window they are counted over — 20 per hour and 5 per minute are both expressible. It counts per sender — the logged-in user where there is one, otherwise the connection IP — and each form type is counted separately. It is off by default, because on sites where many visitors share an address (an office, a school, or mobile carrier NAT) an IP-based limit can catch people who are not doing anything wrong. Set the maximum to 0 to disable it without turning the guard off.

Does this work with Contact Form 7?

Yes. Every Contact Form 7 form on the site is protected the moment the plugin is active, and a Contact Form 7 forms switch appears under Protection targets on the General tab.

There is nothing to add to your forms. You do not place a tag in each form, and there is no per-form setup — forms you build later are covered the same way, with no step to forget.

What runs is the full set of guards rather than a single trick: a hidden field bots fill in, a check that the form was not submitted faster than a person could type, a signature proving the submission came from your site, duplicate detection, an optional rate limit, and your own keyword and link rules. A bot written to step around any one of those still has to get past the rest.

A blocked submission is marked as spam, so the visitor sees the message you configured for spam on that form, and the reason is recorded in Contact Form 7’s own spam log next to the plugin name.

Because Contact Form 7 lets you name fields whatever you like, nothing here matches on field names. It reads the types you built the form from: the message and text fields are screened for content — including the subject line, which spam often targets — and the email field is used as the sender’s address. A field holding nothing but a web address is not counted as a link, whether you built it with a URL field or a plain text one, so asking visitors for their website cannot trip the link limit just by being filled in correctly. Links written among your visitor’s words still count, which is what spam actually looks like.

Does this work with WP Job Manager?

Yes. When WP Job Manager is active the frontend job submission form is protected automatically — including the “save as draft” path — and a WP Job Manager job submissions switch appears under Protection targets on the General tab. WP Job Manager’s own answer to submission spam is Google reCAPTCHA; this gives you the same protection without the third-party service or the puzzle your posters have to solve.

Job listings run longer than comments and legitimately carry more links, so the form gets its own section on the Per-form tab. Two thresholds are worth setting there, and the first is worth doing before you take your first listing: raise the link limit — the default of 3 suits a comment, while an ordinary job description linking to an about page, a benefits page, a team page and an application page already has four. Then raise the minimum submit time well above the site-wide default — nobody writes a job description in three seconds, so a longer minimum costs genuine posters nothing and stops scripted submissions. The company website, video and Twitter fields are never counted as content links, so filling them in normally cannot trip the limit.

Does it replace WordPress’s built-in comment moderation?

No — it complements it. Onsite Spam Guard’s guards run before WordPress’s own comment checks, and those built-ins still run underneath: the duplicate-comment check, the comment flood throttle, the Disallowed Comment Keys blocklist, and the “hold a comment with this many links” setting (all under Settings Discussion). Its Keyword, Link limit, and Duplicate guards overlap those, so you can rely on either or both. What it adds on top is the honeypot, timing, signature, and behavioral checks core has no equivalent for, one settings screen with logging, and protection for WooCommerce reviews and Jetpack contact forms — not just comments.

You can also go the other way and put WordPress’s list to work everywhere: enable Also apply WordPress’s Disallowed Comment Keys on the Guards tab and the plugin runs every protected submission through core’s own blocklist — so the list you already maintain under Settings Discussion starts covering reviews, Jetpack forms, and any form added through the plugin’s API, not only comments.

Does it work with caching plugins?

Yes. The timing and authenticity checks use a token whose signature does not expire (unlike a WordPress nonce, which would go stale on a cached page and block legitimate visitors), so full-page caching does not produce false positives.

Does removing the plugin clean up after itself?

By default, yes — deleting the plugin (not just deactivating it) drops its database table, removes all of its options, clears its scheduled task, and purges its transients, on every site of a multisite network. If you would rather keep your settings and logs (for example, before reinstalling), turn off Delete all plugin data when this plugin is deleted under Spam Guard Settings Logging first.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Onsite Spam Guard” is open source software. The following people have contributed to this plugin.

Contributors

Translate “Onsite Spam Guard” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Changelog

1.5.0

  • Contact Form 7 forms are now protected. Every form is covered the moment the plugin is active — there is no tag to add to each form and no per-form setup, so forms you build later are protected too. A blocked submission is marked as spam, so the visitor sees the spam message you configured for that form, and the reason is recorded in Contact Form 7’s own spam log.
  • WP Job Manager job submissions are now protected, including the “save as draft” path. WP Job Manager’s built-in answer to submission spam is Google reCAPTCHA; this is the same protection with no third-party service and no puzzle for the person posting the job.
  • BuddyPress private messages can now be protected, off by default. BuddyPress applies WordPress’s blocklists to the activity stream but nothing at all to private messages. The checks that suit private messaging are the rate limit and duplicate detection, keyed on the sender rather than on what they wrote, and moderators are never throttled. Message bodies are never written to the spam log — a block records who sent it, when, and which check objected, so private correspondence does not accumulate in a table for an administrator to browse.
  • Each newly protected form gets its own section on the Per-form tab, so a contact form, a job listing and a comment thread can have thresholds suited to each.
  • Fixed: the link limit counted a linked web address twice whenever the link text was the address itself — which is what happens when a visual editor turns a pasted address into a link. An ordinary job description or comment with four links could be counted as five and rejected. Repeated addresses now count once.
  • Fixed: a field holding nothing but a web address is no longer counted as a link in Contact Form 7 forms, whether it was built as a URL field or a plain text one. Asking visitors for their website could otherwise push a genuine message over the link limit.

1.4.0

  • The hidden honeypot field now has a different name on every site, derived from the site’s own secret. Previously every installation used the same name, so a spammer who learned it once could skip that field on every site running this plugin. Nothing to configure, and forms already open in a visitor’s browser keep working.
  • New Per-form settings tab. Guard thresholds used to apply identically to every form on the site, so making a contact form stricter also made the comment form stricter. Thresholds can now be set per form — a comment thread can allow several links while a contact form allows one, and a long application form can require a longer fill time than a comment box. Leave a field blank to use the global value.
  • Plugins that protect their own forms through this plugin can register them so they appear on the Per-form tab. A form that does not register is still protected; it uses the global thresholds.
  • The duplicate-detection window and the rate-limit window are now settable on the Guards tab. Both were fixed at 60 seconds and could only be changed by editing a file inside the plugin, which an update overwrote. A rate limit of “20 per hour” is now expressible, and a busy comment thread where several people legitimately post a short reply within a minute can use a shorter duplicate window instead of turning the guard off.
  • The rate-limit maximum is no longer labelled “per minute”, since the window is no longer always a minute.
  • Number settings are now range-checked when saved. Previously a value outside a field’s range was stored as given, and a negative number was stored as its positive equivalent.

1.3.0

  • The spam log now records every guard that matched a blocked submission, not just the first one. The guard that decided the outcome is still shown on its own line, with any additional matches listed beneath it.
  • Fixed: a submission rejected by one guard was recorded as “seen” by the duplicate guard, so a visitor who corrected the problem and resubmitted was refused a second time — told they had submitted a duplicate rather than what was actually wrong.
  • Fixed: the rate limit was not counting submissions that another guard had already rejected, which is most of what a spam bot sends. A bot could keep submitting indefinitely without ever reaching the limit, while a legitimate visitor who tripped a guard once did count against it. Rejected attempts now count.
  • For developers: other plugins can now register their own guard through the simple_spam_shield_guards filter. A registered guard runs in the pipeline, appears in the log, and gets its own toggle on the Guards tab. A new simple_spam_shield_blocked action fires whenever a submission is blocked. See the readme in the plugin’s repository.

1.2.1

  • Fixed: allowlist entries using an IPv6 range (for example 2001:db8::/32) never matched. IPv4 ranges, exact addresses and email rules were unaffected.
  • Fixed: rate-limit data left rows behind in the database when the plugin was deleted.
  • Fixed: the honeypot guard’s field name is no longer presented as configurable, since changing it disabled the guard rather than renaming the field.

1.2.0

  • New Rate limit guard: throttles repeated submissions from the same sender (the logged-in user where there is one, otherwise the connection IP). Off by default.
  • New option to also apply WordPress’s Disallowed Comment Keys (Settings Discussion) to every protected form, extending that one blocklist beyond comments to reviews, Jetpack forms, and forms added through the plugin’s API.

1.1.3

  • Tested with WordPress 7.1. No functional changes.

1.1.2

  • Renamed to Onsite Spam Guard. The previous name was too generic and overlapped existing plugins in the directory. Your settings and logs are unaffected by the rename.
  • Security hardening: the spam-log bulk-delete action now checks the current user’s capability directly, in addition to verifying the nonce.

1.1.1

  • Fixed: Jetpack contact form submissions could be wrongly flagged. The plugin inspected everything Jetpack passes its spam filter, which includes site and server metadata — the site address, the referrer, the page permalink, and request headers. Those count as links and text, so a legitimate submission containing no links of its own could still exceed the link limit, and keywords could match against the browser’s user-agent string. Only what the visitor actually submitted is inspected now.
  • Fixed: the sender’s name and email were not read from Jetpack submissions, so the keyword guard never checked them and duplicate detection was less accurate.

1.1.0

  • Settings are now organized into tabs (General, Guards, Allowlist, Logging) to reduce scrolling.
  • New setting to control whether all plugin data is removed when the plugin is deleted (on by default).
  • Uninstall now removes the plugin’s table, options, transients, and scheduled task on every site of a multisite network, not just the main site.

1.0.1

  • simple_spam_shield_check() accepts the hidden honeypot/token/behavioral fields explicitly, so REST/AJAX endpoints (JSON body, empty $_POST) can pass them from the request.
  • The time-gate and signature guards skip rather than block when no token is supplied for a custom context, so content-only integrations are not falsely rejected. Built-in comment and review forms still require the token.

1.0.0

  • Initial release.
  • Guard pipeline: honeypot, duplicate detection, time gate, signature, link limit, keyword block, and optional behavioral analysis.
  • Integrations for WordPress comments, WooCommerce product reviews, and Jetpack contact form blocks.
  • Allowlist supporting IPs, CIDR ranges, email addresses, and email domains, with an optional trusted-proxy mode for IP detection.
  • Blocked comments and reviews are routed to the spam queue by default (recoverable), with an option to reject them outright instead.
  • Database-backed logging with a paginated admin viewer and a configurable auto-purge retention window.
  • Public integration API (simple_spam_shield_check / simple_spam_shield_protect_selector / simple_spam_shield_field_markup) so other plugins can protect their own forms.
  • Suggested privacy-policy content and a clean uninstall routine.
  • Developed by Jerome Wincek, with engineering assistance from Anthropic’s Claude.