How I put a stop to spam in Contact Form 7

Contact Form 7 Limit Submissions:

Unfortunately there was no available plugins or codes to limit the submissions in Contact Form 7, so I built one. This is how I built it.

Create Table in Database:

First I created a table in WordPress database. For this article sake let’s name it <strong>wp_cf7_submissions</strong>. Then I created 2 column inside that table and named them “ip” and “email“.

Catch IP and Emails:

Now I needed to catch the IP and Emails submitted by users and save it to the database. In this case I used the <strong>wpcf7_before_send_mail</strong> hook.

add_action( 'wpcf7_before_send_mail', function( $wpcf7 ) {
		
    // specify the contact form id
    if ( $wpcf7->id() != 788 )
	    return $wpcf7;
		
    // get the submission
    $submission = WPCF7_Submission::get_instance();
		
    $remote_ip 	= $submission->get_meta( 'remote_ip' );
    $email      = $submission->get_posted_data()["your-email"];
    
    // save to the wp_cf7_submissions table.
    global $wpdb;
    $wpdb->insert( $wpdb->prefix . "cf7_submissions", array( "ip" => $remote_ip, "email" => $email ) );

    return $wpcf7;

}

In the above functions every IP and Emails will be recorded if it was submitted by the contact form id 788.

Check against the database

Now whenever a contact form id “788” will be submitted we need to cross check the info against the database. I have used <strong>wpcf7_submission_is_blacklisted</strong> hook for that.

add_filter( 'wpcf7_submission_is_blacklisted', function( $value ) {
		
	$submission = WPCF7_Submission::get_instance();
	$posted_data = $submission->get_posted_data();
		
	if ( $posted_data["_wpcf7"] != 788 )
		return $value;
		
	global $wpdb;
	$table = $wpdb->prefix . "cf7_submissions";
		
	$remote_ip = $submission->get_meta( 'remote_ip' );
	$ip_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE ip = '$remote_ip'" );

	$email = $posted_data["your-email"];
	$email_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE email = '$email'" );
		
	// counts starts with zero, so 1 means 2.
	if ( $ip_count > 1 || $email_count > 1 )
		return true;
		
	return $value;
	
});

In this functions if any email or IP exists twice in that table, will be automatically rejected. You can adjust it according to your needs.

If you plan to apply this limit on daily basis then create a cronjob to empty that table everyday. ✌