WordPress
The plugin checks form submissions before WordPress processes them.
Download the WordPress plugin (zip)
Version 1.0.0. Requires WordPress 6.0 and PHP 7.4. Not on wordpress.org yet.
Install
- Add a site in Nolgard and list your WordPress origins, for example
https://example.com. Copy the site key and secret. - In WordPress, go to Plugins → Add New → Upload Plugin. Select the zip and activate the plugin.
- Open Settings → Nolgard. Paste both keys, select the forms to protect and save.
- Click Test connection.
After saving, only the secret's last four characters are shown. Leave the field blank to keep the saved secret. Protection stays off until both keys are set.
Supported forms
- Core: login (
wp-login.php), registration, lost password, comments. - WooCommerce: My Account login and registration, classic checkout.
- Contact Form 7.
- WPForms Lite.
- Fluent Forms.
- Gravity Forms and Elementor Pro Forms. Neither has been tested against live installs yet.
Third-party form checkboxes appear only when the corresponding plugin is active. They start unchecked. Logged-in administrators skip comment checks.
What happens on each decision
| Decision | Result |
|---|---|
allow | The form goes through. |
limit | The form goes through and fires the nolgard_limit action. Use it to reduce what the user gets. |
block | The visitor sees a generic error. |
shadow | Comments go to spam without notifying the visitor. Contact forms show success without sending (details per plugin). Login, registration and password reset return a generic error because they cannot safely fake success. |
Service unreachable
Timeouts, 5xx and over-quota follow this setting. The default is Allow. Choose Block for stricter sites.
Missing tokens and wrong secrets (invalid-secret, HTTP 401) are always rejected, regardless of this setting. A wrong secret also shows an admin notice until you fix it.
Not covered
- XML-RPC, REST API authentication and application passwords.
- Programmatic
wp_authenticate()calls outsidewp-login.php. - Multisite network signup forms.
- WooCommerce block checkout. Use the classic checkout shortcode instead.
- Pingbacks and trackbacks.
- Scripts that call
form.submit()directly and skip the submit event. These need their own integration.
Custom themes must use the standard core form IDs or supply selectors through nolgard_form_selectors.
Behind a proxy or CDN
The plugin trusts only REMOTE_ADDR, never X-Forwarded-For. Behind a load balancer you control, supply the visitor's IP with a filter in a site-specific plugin:
add_filter( 'nolgard_remote_ip', function ( $ip ) {
// Only trust the header when the request came from your own proxy.
if ( '10.0.0.5' === $ip && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
$hops = array_map( 'trim', explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
return end( $hops );
}
return $ip;
} );Don't trust forwarding headers that visitors can set.
Developer hooks
| Hook | Use |
|---|---|
nolgard_remote_ip | Filter. Visitor IP sent for verification. |
nolgard_decision | Filter. Receives the decision, the result (or WP_Error) and a context array with action. |
nolgard_limit | Action. Fires on limit with the result and context. |
nolgard_form_selectors | Filter. Form selectors and action names on the current page. |
nolgard_integrations | Action. Register your own form integration here. |
nolgard_client_url | Filter. Full URL of client.js. |
nolgard_verify() | Function. nolgard_verify( $action, $account ) returns a normalized result or WP_Error. |
// Treat every limit as a block on this site.
add_filter( 'nolgard_decision', function ( $decision, $result, $context ) {
return 'limit' === $decision ? 'block' : $decision;
}, 10, 3 );
To support another form plugin, implement Nolgard\Integration and register it with Nolgard\Integrations::register() on nolgard_integrations. Nolgard\Guard::decide() applies the unreachable policy and the nolgard_decision filter. Keep service diagnostics out of messages shown to visitors.
What the plugin sends
Each protected submission sends the token, visitor IP, action name and, when available, account ID or email. WordPress passwords are never sent. The secret is stored in the options table with autoload off. Only users with manage_options can change it. Uninstalling removes the plugin's options. Read the privacy policy before enabling it.