Form spam is the silent destroyer of HubSpot portals. A single bot finds your demo request form and dumps 4,000 entries over a weekend. By Monday, your lifecycle reports look great, your sender domain is starting to wobble because workflows fired confirmation emails to junk addresses, and your sales team is calling Bobby Tables at gmail.
CAPTCHA stops bots and stops humans. Honeypot stops dumb bots and ignores smart ones. Behavior signals stop most things and require effort. None of them on their own is right. The layered defense is.
What you are actually protecting
Three things, in order.
- Data quality: garbage in your CRM corrodes every report
- Sender reputation: workflows that auto-email garbage hurt deliverability
- Sales time: every fake lead is wasted outreach
Conversion rate is the constraint. Whatever defense you put up has to not torch real submissions. CAPTCHA torches them. So you save CAPTCHA for last.
The layered defense
Three layers, applied in order.
- Honeypot field: free, invisible to humans, stops drive-by bots
- Behavioral signals: time-to-submit, mouse movement, focus events, stops slightly smarter bots
- CAPTCHA: only for forms that still see spam after layer 1 and 2, or for high-value forms
Most forms only need layers 1 and 2. CAPTCHA is the exception.
The honeypot pattern in HubSpot
HubSpot forms allow custom HTML in surrounding markup. Add a hidden field with a name that bots will autofill (like website or phone_2) and absent from real user view. Then build a workflow that rejects submissions where the honeypot was filled.
<style>
.hs-form .hs_hp_url { position: absolute; left: -9999px; height: 0; overflow: hidden; }
</style>
<!-- The HubSpot form, with a custom property hp_url -->
In HubSpot, create a single-line text custom property hp_url. Add it to the form, hide via CSS, and build a workflow:
- Trigger: contact created via form fill, hp_url is known
- Action: set lifecycle stage to “other” and unsubscribe from all, do not notify sales
Do not delete the record. Keep it for analysis. The volume of honeypot hits is the leading indicator that something nastier is coming.
Behavioral signals
A bot fills a form in under a second. A human takes at least 5-10. Capture submission time on the client and pass it as a hidden field.
// On form load
const formLoadTime = Date.now();
// On submit
const hsForm = document.querySelector(".hs-form");
hsForm.addEventListener("submit", (e) => {
const elapsed = (Date.now() - formLoadTime) / 1000;
const elapsedInput = document.createElement("input");
elapsedInput.type = "hidden";
elapsedInput.name = "form_fill_time";
elapsedInput.value = String(elapsed);
hsForm.appendChild(elapsedInput);
const ua = navigator.userAgent;
const uaInput = document.createElement("input");
uaInput.type = "hidden";
uaInput.name = "submit_user_agent";
uaInput.value = ua.slice(0, 200);
hsForm.appendChild(uaInput);
});
Map both to HubSpot properties. Build a workflow that quarantines submissions with form_fill_time under 3 seconds or with a user agent matching known scraper patterns.
// Custom code action
exports.main = async (event, callback) => {
const fillTime = Number(event.inputFields["form_fill_time"] || 0);
const ua = event.inputFields["submit_user_agent"] || "";
const botUA = /headless|phantom|crawler|bot|curl|python-requests/i;
const suspicious = fillTime < 3 || botUA.test(ua);
callback({
outputFields: { spam_score: suspicious ? "high" : "low" },
});
};
Branch the workflow on spam_score. High-score submissions quarantine. Low-score proceeds normally.
When CAPTCHA earns its keep
CAPTCHA belongs on:
- Forms still spammed after honeypot and behavior layers
- High-stakes contact forms (enterprise demo, partnership) where any single false positive is unacceptable
- Forms that hit a daily volume threshold (say, 100 submissions in 24 hours) where the audit cost matters more than the conversion friction
Use Google reCAPTCHA v3 (invisible scoring), not v2 (the “I am not a robot” checkbox). v3 friction is near zero for legitimate users. HubSpot supports both, but v3 requires custom integration.
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>
<script>
grecaptcha.ready(() => {
grecaptcha
.execute("YOUR_SITE_KEY", { action: "submit_form" })
.then((token) => {
document.querySelector('input[name="recaptcha_token"]').value = token;
});
});
</script>
Verify the token server-side via a webhook or custom code action that calls Google’s siteverify endpoint, then sets a recaptcha_score property. Score under 0.5 quarantines.
What CAPTCHA costs you
Even invisible v3 has costs. Privacy regulations in some jurisdictions require disclosure of Google reCAPTCHA in privacy policies. The script adds weight. False positives on shared IP ranges (corporate VPNs, mobile networks) lock out legitimate buyers.
Measure conversion rate before and after. If conversion drops by more than 1% on a high-traffic form, the spam protection costs more than the spam.
What does not work
- IP-based blocking via Cloudflare on its own. Spam farms cycle IPs faster than you can block them.
- Email regex validation. Bots learn the format.
- Required phone number. Bots invent phone numbers.
- “Don’t accept gmail addresses.” Half your real leads use personal email for evaluation.
The thing that consistently works is multi-signal scoring with manual review on the quarantine queue.
Operational rhythm
Quarantine is not a destination, it is a holding cell. Review weekly.
- Truly spam: delete or merge into a “spam” company record for analysis
- False positives: restore to normal lifecycle and tune the rule
- Bot patterns observed: add to UA blocklist
The first month of any new defense, expect 5-10% false positive rate. Tune to 1-2%. Below 1% is over-tuned and probably letting spam through.
Related: popup forms when to skip and form abandonment recovery.
Bottom line
- Honeypot first, behavior second, CAPTCHA only when needed; layered beats blunt.
- Quarantine spam, do not delete; the volume is the early warning.
- v3 invisible CAPTCHA is the only acceptable form when CAPTCHA is required.
- Measure conversion impact every time you tune; the cure can cost more than the disease.
- Weekly review of the quarantine queue is the only thing that keeps the model honest.