Skip to content
FitMyKBVerify

FitMyKB Verify

Add a CAPTCHA to WordPress login, sign-up and comments

Protect wp-login.php, registration and comments from bots with about 40 lines in functions.php, and no plugin to keep updated.

Before you start

Create a free FitMyKB Verify account, add your website and copy its site key and secret. Put both in wp-config.php as constants, so they never end up in a theme you share. While the website is in test mode the key works on localhost and staging domains.

Show the widget on the forms

This loads api.js on the login page and the front end, and adds the box to the login, registration and comment forms. The widget puts a one-time token in each form as fmk-captcha-response.

PHP · functions.php
// In your theme's functions.php, or a small plugin. Put the keys in wp-config.php:
// define('FMK_SITE_KEY', 'your-site-key'); define('FMK_SECRET', 'your-secret');

function fmk_captcha_script() {
	wp_enqueue_script('fmk-captcha', 'https://fitmykb.com/captcha/api.js', [], null, ['strategy' => 'defer']);
}
add_action('login_enqueue_scripts', 'fmk_captcha_script');
add_action('wp_enqueue_scripts', 'fmk_captcha_script');

function fmk_captcha_box() {
	echo '<div class="fmk-captcha" data-sitekey="' . esc_attr(FMK_SITE_KEY) . '"></div>';
}
add_action('login_form', 'fmk_captcha_box');
add_action('register_form', 'fmk_captcha_box');
add_action('comment_form_after_fields', 'fmk_captcha_box');

function fmk_captcha_ok(): bool {
	$reply = wp_remote_post('https://fitmykb.com/captcha/siteverify', ['timeout' => 5, 'body' => [
		'secret' => FMK_SECRET,
		'response' => sanitize_text_field($_POST['fmk-captcha-response'] ?? ''),
	]]);
	if (is_wp_error($reply)) return true; // FitMyKB unreachable: let people in (your choice)
	$data = json_decode(wp_remote_retrieve_body($reply), true);
	return !empty($data['success']);
}

Check the token on the server

Each form is accepted only after siteverify says the token is valid. A token works once, for two minutes, so a bot can't reuse one it saw. If our server can't be reached, the code lets people in; return false instead if you prefer to block.

PHP · server checks
add_filter('authenticate', function ($user) {
	if ($_SERVER['REQUEST_METHOD'] === 'POST' && !fmk_captcha_ok()) {
		return new WP_Error('captcha', 'Please complete the check below the form.');
	}
	return $user;
}, 30);

add_filter('registration_errors', function ($errors) {
	if (!fmk_captcha_ok()) $errors->add('captcha', 'Please complete the check.');
	return $errors;
});

add_filter('preprocess_comment', function ($comment) {
	if (!is_user_logged_in() && !fmk_captcha_ok()) wp_die('Please complete the check and try again.');
	return $comment;
});

Test it

Log out, open wp-login.php and sign in: the box appears under the password. Try once without ticking it and WordPress refuses with your message. Then ask us to switch the website live from your Verify dashboard.

WooCommerce and other forms

WooCommerce's login and register forms run the same authenticate and registration_errors filters, so they're covered. For contact form plugins, add the box with their own hooks and call fmk_captcha_ok() before sending.

Get free keys · Full developer docs

Questions

Good questions

Anything else? Read the docs or ask us.

Does this slow WordPress down?

No. api.js loads after the page, and the check runs in the visitor's browser in about a tenth of a second.

Will it work with my cache plugin?

Yes. The token is made in the browser for each visit, so cached pages stay cacheable.