spatie/laravel-honeypot
Spatie's laravel-honeypot blocks spam form submissions using an invisible honeypot field plus a timestamp check. Just add the x-honeypot Blade component to your form.
Feature-level: add anti-spam to public forms without adding friction.
This package lets any Laravel form be protected by dropping in a single Blade component (<x-honeypot /> or @honeypot) and attaching the ProtectAgainstSpam middleware to the submission route. For a PM, that's a ~1-ticket, no-design-review, no-vendor-onboarding change that reduces inbox pollution and junk records in the CRM/database. It's the cheapest available option before CAPTCHA.
Build vs buy: buy (adopt), because the "build" version is deceptively small.
The core mechanic is a hidden field plus a time-to-submit check — a competent engineer could hand-roll it in a day. What you're actually buying with the MIT dependency is the accumulated edge cases: randomized field names, an encrypted timestamp (so bots can't forge a plausible valid_from), configurable spam responder, and first-party support for Inertia, Livewire (including Volt functional syntax), and Jetstream — plus an enabled env switch that works as a production kill switch. Re-implementing this across every Inertia page and Livewire component is where in-house versions quietly rot.
Roadmap: this defers, and partly de-risks, the CAPTCHA decision. Adopting it gives you a credible answer to "why don't we have CAPTCHA yet?" without committing to a reCAPTCHA/Turnstile integration — which carries vendor cost, third-party script weight, consent/privacy review (GDPR/CCPA), accessibility complaints, and a measurable conversion/friction cost on every form. Treat honeypot as tier-1 defense, rate limiting as tier-2, and CAPTCHA as tier-3 reserved for endpoints with proven, persistent abuse. That's a defensible layered posture you can put in a security or privacy review.
Prioritization: it lets you triage abuse work by endpoint value.
Because rollout is per-route middleware, you can protect the contact form, newsletter signup, and guest auth routes (Auth::routes() wrapped in the middleware) first, measure, then extend. Global middleware registration exists for apps with many controllers, and honeypot_fields_required_for_all_forms can enforce coverage — but that flag should be a deliberate, later-phase decision, not a default, since it will flag any form you forgot to instrument.
Use cases where the business case is strongest:
Metrics this makes legible (and that you should instrument yourself):
The package silently discards spam (default BlankPageResponder) and emits no reporting, so PM-owned instrumentation is required: spam-blocked count vs. total submissions, legitimate-user false positives (watch support tickets saying "the form won't submit"), form completion/abandonment rate before and after rollout, and downstream junk-record rate in the CRM. randomize_name_field_name is on by default and is your main defense against browser autofill and password managers filling the hidden field — monitor that false-positive channel specifically after launch.
Adopt when:
honeypot prop via HandleInertiaRequests.spatie/laravel-csp and need honeypot's inline hidden style to comply (with_csp).Look elsewhere (or layer on top) when:
throttle middleware at minimum.Due-diligence items before you commit it to a roadmap:
SpamResponder.To executives (one paragraph): We can cut the large majority of automated junk submissions across our public forms with a small, free, open-source change to our existing Laravel app — no new vendor, no monthly cost, no third-party scripts on our pages, and no added friction for real users. It works by adding fields real people never see or fill, plus a check that a human actually spent time on the form. It's reversible in one configuration flag, so the downside risk is low. It is not a complete anti-abuse program: it stops generic bots, not determined attackers, so we should pair it with rate limiting and reserve paid bot-detection for endpoints where we see real, persistent abuse. Recommended: approve as a low-cost, low-risk first layer, instrument it, and revisit CAPTCHA only if the data says we need it.
To engineering:
config/honeypot.php.<x-honeypot /> (or @honeypot) to each target form; attach ProtectAgainstSpam to the corresponding POST routes — start per-route, not global.randomize_name_field_name enabled (default) to reduce autofill-driven false positives; keep enabled wired to an env var as a kill switch.Honeypot object through HandleInertiaRequests and merge nameFieldName / validFromFieldName / encryptedValidFrom into each form's payload; for Livewire, use the UsesSpamProtection trait with a HoneypotData property and call protectAgainstSpam() at the top of the submit handler.BlankPageResponder returns a blank response — replace it with a responder that suits your UX and don't leave users staring at nothing on a false positive.honeypot_fields_required_for_all_forms until coverage is audited; it will flag any uninstrumented form, including JSON/API endpoints.To security/legal (brief): No third-party data processor, no user data leaves our infrastructure, no tracking scripts, MIT-licensed with a public changelog and release history, and the anti-spam timing value is encrypted rather than plaintext. It complements — does not replace — rate limiting, WAF, and monitoring.
How can I help you explore Laravel packages today?