spatie/laravel-honeypot
Protect Laravel forms from spam bots with a simple honeypot field and submit-time check. Add the x-honeypot Blade component (or pass values manually for Inertia) and automatically discard suspicious submissions with filled traps or too-fast posts.
## Getting Started
### Minimal Setup
1. **Installation**: Add the package via Composer:
```bash
composer require spatie/laravel-honeypot
Publish Config (Optional):
php artisan vendor:publish --provider="Spatie\Honeypot\HoneypotServiceProvider" --tag="honeypot-config"
(Default config is sufficient for most use cases.)
First Use Case:
<form method="POST" action="{{ route('contact.submit') }}">
<x-honeypot />
<!-- Your form fields -->
</form>
Route::post('/contact', [ContactController::class, 'store'])
->middleware(\Spatie\Honeypot\ProtectAgainstSpam::class);
<x-honeypot /> or @honeypot directives.ProtectAgainstSpam is applied to form-handling routes.config/honeypot.php for field names (name_field_name, valid_from_field_name) and thresholds.Form Protection:
<x-honeypot /> to every public form (contact, registration, etc.).ProtectAgainstSpam middleware to the form’s submission route/controller.Dynamic Field Names:
randomize_name_field_name (default: true) to dynamically rename honeypot fields, reducing bot detection risks.'name_field_name' => 'custom_honeypot_name',
'randomize_name_field_name' => false,
Inertia/Livewire Integration:
return inertia('FormPage', ['honeypot' => \Spatie\Honeypot\Honeypot::class]);
Render hidden fields in Vue:
<input v-if="honeypot.enabled" v-model="form[honeypot.nameFieldName]" type="text" :name="honeypot.nameFieldName">
UsesSpamProtection trait and HoneypotData property:
use Spatie\Honeypot\Http\Livewire\Concerns\{UsesSpamProtection, HoneypotData};
public HoneypotData $extraFields;
public function mount() { $this->extraFields = new HoneypotData(); }
public function submit() { $this->protectAgainstSpam(); /* ... */ }
Global Middleware (Caution):
app/Http/Kernel.php:
protected $middleware = [
\Spatie\Honeypot\ProtectAgainstSpam::class,
// ...
];
<x-honeypot />. Missing fields will trigger spam checks.Custom Spam Responses:
SpamResponder to replace the default blank page:
use Spatie\Honeypot\SpamResponder\SpamResponder;
class CustomResponder implements SpamResponder {
public function respond(): void { abort(403, 'Spam detected.'); }
}
Update config:
'respond_to_spam_with' => \App\CustomResponder::class,
Missing Honeypot Fields:
<x-honeypot />. Omitting it will incorrectly flag legitimate submissions as spam.CSRF + Honeypot Conflicts:
name="token").honeypot_name_123 or leverage randomize_name_field_name.Livewire Volt Quirks:
guessHoneypotDataProperty to avoid property resolution issues:
$guessHoneypotDataProperty = fn() => $this->extraFields;
Time-Based Checks:
amount_of_seconds (default: 1) may be too strict for slow networks.3–5 for user-friendly thresholds:
'amount_of_seconds' => 3,
CSP Integration:
with_csp only if using Laravel CSP. Hidden styles may break without CSP headers.'style-src' => ['self', 'unsafe-inline'], // Temporary workaround
Log Spam Attempts:
SpamProtection to log failed attempts:
use Spatie\Honeypot\Exceptions\SpamException;
class CustomProtection extends \Spatie\Honeypot\SpamProtection {
public function protect(Request $request) {
try { parent::protect($request); }
catch (SpamException $e) { \Log::warning('Spam detected', ['ip' => $request->ip()]); }
}
}
Update config:
'spam_protection' => \App\CustomProtection::class,
Test Locally:
curl:
curl -X POST -d "my_name=spam&valid_from=123" http://your-app.test/contact
Disable Temporarily:
'enabled' => false in config to bypass honeypot during development.Custom Validation:
public function store(Request $request) {
$this->protectAgainstSpam($request);
$validated = $request->validate([
'honeypot_name' => 'required|empty', // Explicitly check honeypot
'email' => 'required|email',
]);
}
Dynamic Field Names:
<x-honeypot :name="'contact_honeypot'" :valid-from="'contact_timestamp'" />
(Requires custom Blade component or config overrides.)Rate Limiting:
throttle middleware to block repeated spam:
Route::post('/contact')->middleware([
\Spatie\Honeypot\ProtectAgainstSpam::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':5,1',
]);
CAPTCHA Fallback:
try { $this->protectAgainstSpam($request); }
catch (SpamException $e) { return redirect()->route('contact')->with('captcha_required', true); }
---
How can I help you explore Laravel packages today?