spatie/laravel-honeypot
Protect Laravel forms from spam bots with a simple honeypot + timed submission check. Add the x-honeypot Blade component (or pass values manually for Inertia) and the package will reject requests with filled honeypot fields or unrealistically fast submits.
Installation:
composer require spatie/laravel-honeypot
Publish the config file (optional):
php artisan vendor:publish --provider="Spatie\Honeypot\HoneypotServiceProvider" --tag="honeypot-config"
Add Honeypot to a Form: Include the Blade component in your form:
<form method="POST" action="{{ route('contact.submit') }}">
<x-honeypot />
<!-- Your form fields -->
</form>
Apply Middleware: Protect the route handling the form submission:
Route::post('/contact', [ContactController::class, 'store'])
->middleware(\Spatie\Honeypot\ProtectAgainstSpam::class);
Scenario: Protect a contact form from spam submissions.
<x-honeypot /> to the form.Blade Integration:
<x-honeypot /> or @honeypot directive for standard Blade forms.return inertia('FormPage', [
'honeypot' => \Spatie\Honeypot\Honeypot::class,
]);
Render in Vue:
<div v-if="honeypot.enabled">
<input type="text" v-model="form[honeypot.nameFieldName]" :name="honeypot.nameFieldName" />
<input type="text" v-model="form[honeypot.validFromFieldName]" :value="honeypot.encryptedValidFrom" />
</div>
Livewire Integration:
UsesSpamProtection trait to your component.HoneypotData property and call protectAgainstSpam() in submission logic:
use Spatie\Honeypot\Http\Livewire\Concerns\{UsesSpamProtection, HoneypotData};
class ContactForm extends Component {
use UsesSpamProtection, HoneypotData;
public function mount() {
$this->extraFields = new HoneypotData();
}
public function submit() {
$this->protectAgainstSpam(); // Throws exception if spam detected
// Process form...
}
}
<x-honeypot livewire-model="extraFields" /> in Blade.Global Middleware:
ProtectAgainstSpam in app/Http/Kernel.php for all routes:
protected $middleware = [
// ...
\Spatie\Honeypot\ProtectAgainstSpam::class,
];
<x-honeypot />.Custom Spam Responses:
Spatie\Honeypot\SpamResponder\SpamResponder:
use Spatie\Honeypot\SpamResponder\SpamResponder;
class CustomResponder implements SpamResponder {
public function respond(): void {
abort(403, 'Spam detected!');
}
}
'respond_to_spam_with' => \App\SpamResponders\CustomResponder::class,
required rules) for user-facing fields.Spatie\Honeypot\Tests\TestCases\HoneypotTestCase for unit tests.with_csp in config and install spatie/laravel-csp to handle inline styles for hidden fields.Missing Honeypot Fields:
honeypot_fields_required_for_all_forms: true), all forms must include <x-honeypot />. Omitting it will flag legitimate submissions as spam.Timestamp Validation:
amount_of_seconds: 1 may block legitimate users with fast connections. Increase this value (e.g., 5) if users report false positives.valid_from_timestamp in submitted data to verify timing logic.Inertia/Livewire Edge Cases:
honeypot object is passed to every form page. Forgetting it causes missing fields.HoneypotData property must be initialized in mount() and referenced in Blade (livewire-model="extraFields").CSRF Conflicts:
Dynamic Forms:
<x-honeypot /> is rendered before form submission. Use AJAX pre-submission checks if needed.Log Spam Attempts: Wrap the middleware in a try-catch to log spam:
try {
$this->middleware(ProtectAgainstSpam::class)->handle($request);
} catch (\Spatie\Honeypot\Exceptions\SpamException $e) {
\Log::warning('Spam detected', ['data' => $request->all()]);
throw $e;
}
Verify Field Names:
Check config/honeypot.php for name_field_name and valid_from_field_name. Mismatches (e.g., due to caching) cause false negatives.
Test with Real Bots: Use tools like Sucuri SiteCheck to simulate bot submissions and validate detection.
Custom Field Names: Override config values dynamically:
config(['honeypot.name_field_name' => 'custom_name_' . uniqid()]);
Conditional Protection: Disable honeypot for specific routes:
Route::post('/admin/contact', [ContactController::class])
->middleware('honeypot:disable'); // Custom middleware to skip protection
Whitelisting IPs:
Extend ProtectAgainstSpam to bypass checks for trusted IPs:
public function handle($request, Closure $next) {
if ($this->isTrustedIp($request->ip())) {
return $next($request);
}
// ... rest of logic
}
Rate Limiting: Combine with Laravel’s rate limiting to throttle spam further:
Route::post('/contact', [ContactController::class])
->middleware(['honeypot', 'throttle:10,1']);
honeypot.enabled: false to isolate impact.randomize_name_field_name: true to vary honeypot field names and reduce bot adaptation.How can I help you explore Laravel packages today?