As of December 2025, the code below will completely stop all bot spam from the HTML Forms contact form submissions.
To use with another plugin, look for the available filters to hook into before the form submits. You’ll also most likely need to update the $message and $name variables.
Simply copy and paste the code in your functions.php file or custom plugin.
<?php
/**
* Custom Honeypot and Spam Protection for HF Forms
*
* This function adds multiple layers of spam protection to forms created with the HTML Forms plugin.
* It includes a honeypot field, pattern matching for common spam characteristics, and a timestamp check
* to ensure that the form is not submitted too quickly.
* @return string An error code if validation fails, or an empty string if validation passes.
*/
add_filter('hf_validate_form', function($error_code) {
// --- 1. Smart Honeypot Check ---
if (!empty($_POST['website'])) {
return 'honeypot_failed';
}
// --- 2. Pattern Matching---
$message = isset($_POST['ID_LIKE_YOUR_HELP_WITH']) ? trim($_POST['ID_LIKE_YOUR_HELP_WITH']) : '';
$name = isset($_POST['MY_NAME_IS']) ? trim($_POST['MY_NAME_IS']) : '';
// Rule A: The "One Long Word" check
// If message is longer than 15 chars but has NO spaces, it's likely a bot or hash.
if (strlen($message) > 15 && strpos($message, ' ') === false) {
return 'spam_pattern_message';
}
// Rule B: Name length check
// If the name is longer than 20 chars and has NO spaces, it's a bot.
if (strlen($name) > 20 && strpos($name, ' ') === false) {
return 'spam_pattern_name';
}
// Rule C: Link limiting
// If the message contains 'http' or 'www' more than once, flag it (optional)
if (substr_count(strtolower($message), 'http') > 1) {
return 'spam_too_many_links';
}
// --- 3. Timestamp Check ---
if (empty($_POST['form_start_time'])) {
return 'missing_timestamp';
}
// Calculate time difference
$start = floatval($_POST['form_start_time']); // Client JS time
$now = round(microtime(true) * 1000); // Server PHP time
$seconds = ($now - $start) / 1000;
// If it took less than 3 seconds, it's inhumanly fast.
if ($seconds < 3) {
return 'submitted_too_fast';
}
return ''; // All checks passed
});
Add the HTML and JavaScript files below to your form's HTML.
<div class="website-field-wrapper" aria-hidden="true">
<label for="website">Website</label>
<input type="text" name="website" id="website" autocomplete="off">
</div>
<input type="hidden" name="form_start_time" id="form_start_time">
<input type="hidden" name="js_enabled" id="js_enabled">
<input class="contact-form__submit button" type="submit" value="Send" />
<script>
document.addEventListener("DOMContentLoaded", function () {
document.getElementById("js_enabled").value = "1";
document.getElementById("form_start_time").value = Date.now();
});
</script>
CSS to hide the website honeypot field
.website-field-wrapper {
opacity: 0;
position: absolute;
top: 0;
left: 0;
height: 0;
width: 0;
z-index: -1;
}
To test if it works, either open your dev tools and manually add a value attribute to the website field and try submitting the form or temporarily remove (or comment out) the CSS that hides the field. If it submits, then something is not working. If you get an error, then you know it’s working.