VERIFIED.NET
Guides · WordPress

How to stop fake signups and form spam in WordPress

Updated 2026-10-19 · 6 min read

Every WordPress site with a registration page or a contact form gets junk: bot registrations with random Gmail addresses, contact forms full of SEO offers, newsletter signups from @mailinator.com. No single fix stops all of it. A few layers, each cheap, stop nearly all of it without making real visitors solve puzzles.

The layers, cheapest first

1. Turn off what you don't use

If your site doesn't need user accounts, untick Settings → General → Membership: Anyone can register. Most fake signups disappear with that one checkbox. Close comments on old posts too (Settings → Discussion).

2. A honeypot field

A honeypot is a form field hidden from people but visible to bots, which fill in everything. Any submission with that field filled is a bot. It's invisible, free and catches most simple bots. Contact Form 7, WPForms and Gravity Forms all have honeypot options or add-ons; turn them on.

3. Rate limits

Limit how many submissions one IP address can make per hour. Security plugins (Wordfence, Solid Security and others) include login and registration rate limiting. Five registrations an hour from one address is never a person.

4. Validate the email and phone themselves

This is the layer that catches what gets past the others: real people (or paid click farms) typing fake details, and bots smart enough to skip honeypots. A WordPress form's built-in "email" field only checks the format, so asdf@asdf.com, jane@gmial.com and throwaway inboxes all pass. A real check looks at:

  • whether the domain receives mail at all,
  • whether it's a disposable (temporary) inbox (more on these),
  • whether the mailbox exists, where the mail server will say,
  • for phone numbers: whether the number is valid and assigned, and what kind of line it is (VoIP detection).

5. CAPTCHA, as a last resort

CAPTCHAs work but cost you real submissions, especially on phones, and most send visitor data to a third party, which you then have to mention in your privacy policy. Try the layers above first; add a CAPTCHA only to the form that's still being abused.

Validating with the Verified.net plugin

Our free WordPress plugin, Verified.net Email & Phone Validation, adds layer 4 to Contact Form 7, WPForms, Gravity Forms and the WordPress registration form, with no code. It blocks addresses and numbers the API marks reject, lets everything through if the API can't be reached, and caches results for a day. Install it from Plugins → Add New by searching for "Verified.net", or from WordPress.org, then paste your API key under Settings → Verified.net.

A code snippet instead

If you'd rather not add a plugin, the snippet below does the same for the registration form and Contact Form 7. Add this to a small custom plugin or with a code-snippets plugin (not to a theme's functions.php, which a theme update overwrites). Put your API key in wp-config.php as define( 'VERIFIED_NET_KEY', '...' );.

php
<?php
/**
 * Reject emails the Verified.net API marks "reject" (disposable, no mail server,
 * mailbox not found, invalid). Fails open if the API can't be reached.
 */
function vn_email_error( $email ) {
    if ( ! defined( 'VERIFIED_NET_KEY' ) || '' === $email ) {
        return '';
    }
    $cache_key = 'vn_' . md5( strtolower( $email ) );
    $result    = get_transient( $cache_key );
    if ( false === $result ) {
        $response = wp_remote_post( 'https://api.verified.net/v1/email', array(
            'timeout' => 8,
            'headers' => array(
                'Authorization' => 'Bearer ' . VERIFIED_NET_KEY,
                'Content-Type'  => 'application/json',
            ),
            'body'    => wp_json_encode( array( 'address' => $email ) ),
        ) );
        if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
            return '';                                   // fail open
        }
        $result = json_decode( wp_remote_retrieve_body( $response ), true );
        set_transient( $cache_key, $result, DAY_IN_SECONDS );
    }
    if ( ! empty( $result['did_you_mean'] ) ) {
        return sprintf( 'Did you mean %s?', $result['did_you_mean'] );
    }
    if ( isset( $result['recommendation']['action'] ) && 'reject' === $result['recommendation']['action'] ) {
        return 'Please use a working, permanent email address.';
    }
    return '';
}

// WordPress registration form.
add_filter( 'registration_errors', function ( $errors, $login, $email ) {
    $error = vn_email_error( $email );
    if ( '' !== $error ) {
        $errors->add( 'vn_email', $error );
    }
    return $errors;
}, 20, 3 );

// Contact Form 7 email fields.
foreach ( array( 'wpcf7_validate_email', 'wpcf7_validate_email*' ) as $hook ) {
    add_filter( $hook, function ( $result, $tag ) {
        $value = isset( $_POST[ $tag->name ] ) ? sanitize_email( wp_unslash( $_POST[ $tag->name ] ) ) : '';
        $error = vn_email_error( $value );
        if ( '' !== $error ) {
            $result->invalidate( $tag, $error );
        }
        return $result;
    }, 20, 2 );
}

Each check costs 1 credit; the one-day cache means a visitor resubmitting the form isn't charged twice, and the API itself doesn't charge again for the same address within 7 days.

What to expect

  • Honeypot plus rate limits typically remove most automated junk on their own.
  • Email validation then removes most of what's left: the throwaway inboxes, typos and made-up addresses that people type by hand.
  • Keep an eye on your form plugin's entries for a week after each change to make sure real visitors still get through.

Try it on your own data

Check an address or number now with the free checker, no signup needed, or start a 7-day trial and call the API from your code.

Start free trial API docs