Honeypot Spam Protection
Protect your forms from spam bots with an invisible honeypot field.
Note: Forminit includes built-in spam protection by default. Honeypot is an optional extra layer of protection for forms that receive high volumes of spam.
How It Works
Section titled “How It Works”Honeypot protection exploits a key difference between humans and bots:
- Humans don’t see or interact with hidden fields
- Bots automatically fill out every field they find, including hidden ones
When a submission includes a value in the honeypot field, Forminit flags it as spam and rejects the submission. Legitimate users will never trigger this because they can’t see or fill the hidden field.
Quick Start
Section titled “Quick Start”Add a hidden honeypot field to your form:
<form id="contact-form">
<input type="text" name="fi-sender-firstName" placeholder="First name" required />
<input type="text" name="fi-sender-lastName" placeholder="Last name" required />
<input type="email" name="fi-sender-email" placeholder="Email" required />
<textarea name="fi-text-message" placeholder="Message" required></textarea>
<input type="hidden" name="_gotcha" style="display:none !important" />
<button type="submit">Send</button>
</form>
That’s it! Any bot that fills in the _gotcha field will have their submission automatically rejected.
Field Requirements
Section titled “Field Requirements”| Attribute | Value | Required |
|---|---|---|
type | hidden | Yes |
name | _gotcha (default) | Yes |
style | display:none !important | Recommended |
value | Empty (no value) | Yes |
Important notes:
- The field must have no value — leave it empty
- Use both
type="hidden"andstyle="display:none !important"for maximum protection - Do not add
requiredattribute to the honeypot field
Custom Honeypot Field Name
Section titled “Custom Honeypot Field Name”By default, the honeypot field is named _gotcha. Some sophisticated bots recognize common honeypot names and avoid them. You can use a custom field name for better protection.
Change the Field Name
Section titled “Change the Field Name”- Go to your Form Settings
- Navigate to Honeypot
- Enter your custom field name
- Save changes
Update Your Form
Section titled “Update Your Form”After changing the field name in settings, update your HTML to match:
<input type="hidden" name="website_url" style="display:none !important" />
Tips for custom field names:
- Use names that look like real fields (e.g.,
website,company_url,fax_number) - Avoid obvious names like
honeypot,trap, orbot_check - Don’t use the
fi-prefix for honeypot fields
Framework Examples
Section titled “Framework Examples”HTML / Static Website
Section titled “HTML / Static Website”<form id="contact-form">
<input type="text" name="fi-sender-firstName" placeholder="First name" required />
<input type="text" name="fi-sender-lastName" placeholder="Last name" required />
<input type="email" name="fi-sender-email" placeholder="Email" required />
<textarea name="fi-text-message" placeholder="Message" required></textarea>
<input type="hidden" name="_gotcha" style="display:none !important" />
<button type="submit">Send</button>
</form>
<p id="form-result"></p>
<script src="https://forminit.com/sdk/v1/forminit.js"></script>
<script>
const forminit = new Forminit();
const FORM_ID = 'YOUR_FORM_ID';
document.getElementById('contact-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const { data, error } = await forminit.submit(FORM_ID, formData);
if (error) {
document.getElementById('form-result').textContent = error.message;
return;
}
document.getElementById('form-result').textContent = 'Message sent!';
e.target.reset();
});
</script>
Next.js
Section titled “Next.js”'use client';
import { useState } from 'react';
import { Forminit } from 'forminit';
export function ContactForm() {
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
const [error, setError] = useState<string | null>(null);
const forminit = new Forminit({ proxyUrl: '/api/forminit' });
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus('loading');
setError(null);
const formData = new FormData(e.currentTarget);
const { data, error } = await forminit.submit('YOUR_FORM_ID', formData);
if (error) {
setStatus('error');
setError(error.message);
return;
}
setStatus('success');
e.currentTarget.reset();
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="fi-sender-firstName" placeholder="First name" required />
<input type="text" name="fi-sender-lastName" placeholder="Last name" required />
<input type="email" name="fi-sender-email" placeholder="Email" required />
<textarea name="fi-text-message" placeholder="Message" required />
<input type="hidden" name="_gotcha" style={{ display: 'none' }} />
{status === 'error' && <p className="error">{error}</p>}
{status === 'success' && <p className="success">Message sent!</p>}
<button type="submit" disabled={status === 'loading'}>
{status === 'loading' ? 'Sending...' : 'Send'}
</button>
</form>
);
}
Nuxt.js
Section titled “Nuxt.js”<script setup lang="ts">
import { ref } from 'vue';
import { Forminit } from 'forminit';
const FORM_ID = 'YOUR_FORM_ID';
const status = ref<'idle' | 'loading' | 'success' | 'error'>('idle');
const errorMessage = ref<string | null>(null);
const formRef = ref<HTMLFormElement | null>(null);
const forminit = new Forminit({ proxyUrl: '/api/forminit' });
async function handleSubmit() {
if (!formRef.value) return;
status.value = 'loading';
errorMessage.value = null;
const formData = new FormData(formRef.value);
const { data, error } = await forminit.submit(FORM_ID, formData);
if (error) {
status.value = 'error';
errorMessage.value = error.message;
return;
}
status.value = 'success';
formRef.value.reset();
}
</script>
<template>
<form ref="formRef" @submit.prevent="handleSubmit">
<input type="text" name="fi-sender-firstName" placeholder="First name" required />
<input type="text" name="fi-sender-lastName" placeholder="Last name" required />
<input type="email" name="fi-sender-email" placeholder="Email" required />
<textarea name="fi-text-message" placeholder="Message" required />
<input type="hidden" name="_gotcha" style="display: none !important" />
<p v-if="status === 'error'" class="error">{{ errorMessage }}</p>
<p v-if="status === 'success'" class="success">Message sent!</p>
<button type="submit" :disabled="status === 'loading'">
{{ status === 'loading' ? 'Sending...' : 'Send' }}
</button>
</form>
</template>
Node.js (Server-side)
Section titled “Node.js (Server-side)”When building forms programmatically on the server, include the honeypot field:
import express from 'express';
import { Forminit } from 'forminit';
const app = express();
app.use(express.urlencoded({ extended: true }));
const forminit = new Forminit({
apiKey: process.env.FORMINIT_API_KEY,
});
const FORM_ID = 'YOUR_FORM_ID';
app.post('/submit', async (req, res) => {
const formData = new FormData();
// Add form fields
formData.append('fi-sender-firstName', req.body.firstName);
formData.append('fi-sender-lastName', req.body.lastName);
formData.append('fi-sender-email', req.body.email);
formData.append('fi-text-message', req.body.message);
if (req.body._gotcha) {
formData.append('_gotcha', req.body._gotcha);
}
const { data, error } = await forminit.submit(FORM_ID, formData);
if (error) {
return res.status(400).json({ success: false, message: error.message });
}
res.json({ success: true, submissionId: data.hashId });
});
app.listen(3000);
Combining with Other Protection
Section titled “Combining with Other Protection”For maximum spam protection, combine honeypot with other methods:
| Method | Best For | User Impact |
|---|---|---|
| Honeypot | Basic bot protection | None (invisible) |
| reCAPTCHA | Advanced bot protection | Low (invisible v3) to Medium (checkbox) |
| hCaptcha | Privacy-focused protection | Medium (requires interaction) |
| Rate Limiting | Preventing abuse | None (automatic) |
Example combining honeypot with reCAPTCHA:
<form id="contact-form">
<input type="text" name="fi-sender-firstName" placeholder="First name" required />
<input type="email" name="fi-sender-email" placeholder="Email" required />
<textarea name="fi-text-message" placeholder="Message" required></textarea>
<!-- Honeypot -->
<input type="hidden" name="_gotcha" style="display:none !important" />
<!-- reCAPTCHA -->
<div class="g-recaptcha" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Send</button>
</form>
Common Issues
Section titled “Common Issues”Submissions incorrectly flagged as spam
Section titled “Submissions incorrectly flagged as spam”If legitimate submissions are being blocked:
- Check for autofill — Some password managers or browser extensions might fill hidden fields
- Verify the field is hidden — Ensure both
type="hidden"andstyle="display:none"are set - Check for default values — Make sure the honeypot field has no value attribute
Bots bypassing honeypot
Section titled “Bots bypassing honeypot”Sophisticated bots may avoid honeypot fields. Consider:
- Use a custom field name — Change from the default
_gotchato something less obvious - Add reCAPTCHA or hCaptcha — Combine honeypot with CAPTCHA for stronger protection
- Use CSS hiding — Some bots only check for
type="hidden", not CSS visibility
Field not working
Section titled “Field not working”Verify your implementation:
<!-- ✅ Correct -->
<input type="hidden" name="_gotcha" style="display:none !important" />
<!-- ❌ Wrong - has fi- prefix -->
<input type="hidden" name="fi-_gotcha" style="display:none !important" />
<!-- ❌ Wrong - has a value -->
<input type="hidden" name="_gotcha" value="test" style="display:none !important" />
<!-- ❌ Wrong - missing style -->
<input type="hidden" name="_gotcha" />
Related Documentation
Section titled “Related Documentation”- reCAPTCHA Integration — Google reCAPTCHA setup
- hCaptcha Integration — Privacy-focused CAPTCHA
- Form Blocks Reference — Complete reference for all field types
- HTML Integration — Static site setup guide