WP Manifestindependent plugin directory
manifest / forms / method-gf-shadow-ban

Method – Shadow Ban for Gravity Forms

Shadow-bans form submitters by email address or IP. Matching entries are saved and flagged, the submitter still receives any notification addressed to them, and every other notification and add-on feed is silently suppressed.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/pixelwatt/method-gf-shadow-ban/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

Gravity Forms add-on that shadow-bans submitters by email address or IP.

A matched submission looks completely normal from the sender's side — the form submits, the confirmation shows, and any notification addressed to the submitter is delivered — but nobody else is notified, add-on feeds are (optionally) skipped, and the entry is flagged in the admin UI.

Requires Gravity Forms 2.5+ and PHP 7.4+.

How it works

Everything hangs off the normal GF submission pipeline; nothing is intercepted before the entry is saved.

  1. Entry is saved as usual (GFFormsModel::save_lead).

  2. GFFormsModel::set_entry_meta() runs the add-on's registered update_entry_meta_callbacks. The submission is tested against the lists once, and the result is written to two entry-meta keys:

    • mgfsb_match — human-readable reason (Email: x@y.com (rule @y.com) / IP: 203.0.113.9 (rule 203.0.113.0/24)), empty when clean. Shown as the "Shadow Ban" column.
    • mgfsb_flagged — 1/0, so the entries filter dropdown can do "Shadow Banned (1/0)" is "1".
  3. gform_entry_post_save → feed add-ons run. gform_addon_pre_process_feeds hands them an empty feed list for matched entries (option, on by default).

  4. Notifications → every email passes through gform_pre_send_email. For a matched entry the add-on reduces To, Cc and Bcc to addresses the submitter typed into the form's Email fields; if nothing is left the email is aborted (abort_email). So:

    • the admin notification (To: {admin_email}, staff@…) → aborted;
    • the user confirmation (To: {Email:3}) → delivered;
    • a user confirmation that BCCs an archive address → delivered, BCC stripped.

    Addresses that are themselves configured notification recipients on that form (static To, routing rule emails, Cc/Bcc) and the site admin_email are never treated as "the submitter", so typing the admin's address into the Email field doesn't smuggle a "confirmation" into the admin's inbox.

  5. gform_after_submission → a note is added to the entry recording what was suppressed (option, on by default).

The on-screen confirmation is never touched.

Because the decision is stored on the entry at save time, later changes to the lists don't retroactively re-flag old entries, and a resend of an old entry uses the stored decision. Entries that predate the add-on (no meta) are evaluated live.

Settings

Forms → Settings → Shadow Ban (capability gravityforms_edit_settings).

Two textareas, one rule per line. # starts a comment; blank lines are ignored. Invalid lines are reported (red icon + status box) but don't block saving — the valid rules still apply.

Rule Matches
user@example.com that address (case-insensitive)
@example.com any address at exactly that domain (shorthand for *@example.com)
*@*.example.com glob — * any run of characters, ? one character; works in the local part too (*+promo@*)
203.0.113.7 exact IPv4
2001:db8::1 exact IPv6 (any textual form; compared in binary, zone IDs ignored)
203.0.113.0/24, 2001:db8::/32 CIDR
203.0.113.* IPv4 trailing wildcard, 1–3 octets then *

IPv4-mapped IPv6 (::ffff:203.0.113.7) from dual-stack servers collapses to IPv4 before matching.

Options:

  • Also skip add-on feeds (default on) — webhooks, Zapier, Slack, CRM, mailing-list, etc. via gform_addon_pre_process_feeds.
  • Add a note to each matched entry (default on).
  • Loose email matching (default off) — strips +tags on every domain, and dots in Gmail local parts (googlemail.com → gmail.com), on both the rules and the submitted value.

Admin UI

  • Entries list: "Shadow Ban" column with a red badge (enabled by default on forms that haven't customised their columns; add it from the column picker otherwise). Filterable via the standard filter dropdown.
  • Entry detail: warning banner at the top of matched entries; a Shadow Ban sidebar box showing the status with one-click Block <email> / Block IP <ip> links. Clicking appends the value to the list (with a # added YYYY-MM-DD from entry #N by user comment) and immediately re-flags that entry.
  • Notes: a Shadow Ban note on matched entries.

What the admin can still do

GF's own Resend Notifications (entry sidebar and entries-list bulk action, AJAX action gf_resend_notifications) by a user with gravityforms_edit_entries bypasses suppression — that's the deliberate "I looked at it and I want it sent" path. Nothing else is bypassed: GF's background feed processor also runs through admin-ajax.php and forwards the browser's cookies, so a looser is_admin() check would leak notifications whenever a logged-in admin tests a form.

Known limits

  • Payment add-ons (GFPaymentAddOn) select their feed during validation, before gform_addon_pre_process_feeds runs, so a payment can still be authorised/captured for a matched entry. Treat shadow-banning as a notification/feed control, not a payment control.
  • The submitter's address has to be in an Email-type field for a confirmation to pass. If a form collects the address in a plain text field, every notification for a matched entry is suppressed (which is the safe failure).
  • GF's "Prevent IP storage" setting leaves entry.ip empty. The add-on falls back to the live request IP during the submission itself, so IP rules still apply at submit time; IP-based re-evaluation later isn't possible for those entries.
  • Matching is against the IP GF records (GFFormsModel::get_ip(), which honours gform_ip_address). Behind a proxy/CDN make sure GF is seeing the client IP, not the edge.
  • # in an email local part is treated as a comment marker.

Hooks

// Override/extend the match decision. Non-empty string = ban with that reason; '' = allow.
add_filter( 'method_gf_shadow_ban_match', function ( $reason, $entry, $form ) { return $reason; }, 10, 3 );

// Adjust which addresses a matched submitter may still receive mail at (lower-cased address => true).
add_filter( 'method_gf_shadow_ban_allowed_recipients', function ( $allowed, $entry, $form, $notification ) { return $allowed; }, 10, 4 );

// Widen/narrow the admin bypass for a given email.
add_filter( 'method_gf_shadow_ban_allow_manual_resend', function ( $allow, $email, $notification, $entry ) { return $allow; }, 10, 4 );

// Per-entry override of the feed suppression option.
add_filter( 'method_gf_shadow_ban_suppress_feeds', function ( $suppress, $feeds, $entry, $form ) { return $suppress; }, 10, 4 );

Programmatic access: method_gf_shadow_ban()->get_match( $entry ) returns the reason string (empty when clean); ->get_matcher() returns the parsed rule set.

Entries can be queried by meta: GFAPI::get_entries( $form_id, array( 'field_filters' => array( array( 'key' => 'mgfsb_flagged', 'value' => '1' ) ) ) ).

Logging goes through the add-on's GF logger (Forms → Settings → Logging → Shadow Ban).

Install / update

Upload the zip through Plugins → Add New, or drop the method-gf-shadow-ban directory into wp-content/plugins. The main file carries GitHub Plugin URI: pixelwatt/method-gf-shadow-ban / Primary Branch: main headers for Git Updater — change or remove them if the repo lives elsewhere.

Uninstalling (either the Plugins screen or Forms → Settings → Uninstall) removes the settings option and the two entry-meta keys from all entries.

Tests

No WordPress or GF install needed:

php tests/test-matcher.php   # rule parsing + email/IP/CIDR matching
php tests/test-addon.php     # suppression logic against a WP/GF stub layer