Download the PHP package itxshakil/laravel-form-shield without Composer
On this page you can find all versions of the php package itxshakil/laravel-form-shield. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download itxshakil/laravel-form-shield
More information about itxshakil/laravel-form-shield
Files in itxshakil/laravel-form-shield
Package laravel-form-shield
Short Description CAPTCHA-free spam scoring for Laravel forms: quarantine instead of reject, keep every verdict, and tune your rules against human-labelled data.
License MIT
Homepage https://github.com/itxshakil/laravel-form-shield
Informations about the package laravel-form-shield
Laravel Form Shield
Spam scoring for Laravel forms, with no CAPTCHA. Suspected spam is quarantined instead of rejected, and the rules are tuned against your own labelled data.
A honeypot catches the dumbest bots. A CAPTCHA catches more, but it costs you real visitors, a third-party script, and a privacy question. Rejecting suspected spam outright quietly throws away the occasional real lead, and nobody ever finds out.
Form Shield is the layer after the honeypot:
- It scores, it doesn't guess. Hard signals (honeypot, forged token, too fast) flag on their own. Soft signals (no JavaScript, duplicate message, disposable or dead email domain, link in the name field, gibberish...) add up to a threshold, and no single soft signal can quarantine anyone.
- It quarantines. A flagged submission gets the normal success page, but no email goes out and no side effects run. You keep the row. Bots learn nothing, and a false positive is one click to recover.
- It learns from your labels. Every verdict is stored, spam or not. When someone marks a submission as spam or ham,
php artisan form-shield:reportshows each signal's precision, so you tune weights from data instead of hunches.
Example report output:
It works with Blade forms, Livewire, Inertia/SPA forms, JSON APIs, and forms embedded in iframes on other sites.
Requirements
- PHP 8.2+
- Laravel 12 or 13
Install
The installer publishes the config, then asks where verdicts should be stored:
- on a table you already have (
inquiries,leads...): it writes a migration that adds the spam columns, or - in Form Shield's own
form_shield_submissionstable, for forms that don't store their posts anywhere (say, a contact form that only sends an email).
For scripts and CI: php artisan form-shield:install --table=inquiries --migrate or --log --migrate.
Quick start
1. Add the shield to your form, and the script once in your layout.
2. Store verdicts. If you picked an existing table, the installer already wrote the migration. You can also run it on its own:
Then add the trait to the model:
3. Inspect, store, and skip the side effects for spam.
Don't write
Inquiry::create([...$data, ...$verdict->toAttributes()]). If your model lists its fields in$fillable, Laravel silently drops the spam columns and every row is saved as "not spam".createWithVerdict()fills$datanormally and force-fills only the verdict columns. The two-step equivalent isInquiry::make($data)->applyVerdict($verdict)->save().
No table of your own? Use the built-in log instead of the Inquiry model:
Prefer match? Every verdict has a status enum:
4. Label some submissions, then read the report.
Optional: reject obvious spam content
The scoring above never shows an error, because the point is that bots can't tell what was caught. A few patterns are different: a real person can fix them, so it's kinder to say so. NoSpamContent is a normal validation rule for those:
It fails with a visible error for more than 2 links, HTML or BBCode link markup, or a configured phrase like "quality backlinks". Leave it out if you'd rather everything went through quarantine. See signals.
Prefer middleware?
The middleware applies the rate caps up front (a 429) and, if you turn it on, rejects expired forms (a 422). It never rejects spam, because quarantine is your controller's job. The inspection itself runs on the first spamVerdict() call, so call it after validation. A visitor who fixes a typo and resubmits shouldn't be scored as a duplicate of their own first attempt. The same applies when you call FormShield::inspect() yourself.
Livewire
Inertia, SPAs, and cross-origin forms
Share FormShield::fields() as a prop, or enable the JSON endpoint (form-shield.route.enabled). Then arm the shield on the form's first interaction:
Testing your app
Documentation
- How it works: hard vs soft signals, the exemption, and why quarantine
- Configuration
- Profiles: different rules per form
- Signals reference
- Custom signals
- Storing verdicts and reviewing
- Tuning with the report
- Livewire
- JSON, SPA, Inertia and iframe forms
- Rate limits
- Events
- Testing your app
- Accessibility and privacy
- FAQ
How it compares
| Honeypot packages | CAPTCHA (reCAPTCHA, hCaptcha, Turnstile) | Form Shield | |
|---|---|---|---|
| Friction for real people | none | a challenge, sometimes | none |
| Third-party script / privacy review | no | yes | no |
| Catches bots that skip the honeypot | rarely | usually | often, via scoring |
| What happens on a false positive | lead lost | lead lost | quarantined, recoverable |
| Tells you which rules are wrong | no | no | yes, precision per signal |
Form Shield isn't a CAPTCHA replacement for high-value targets like login or signup under attack. It's for contact, quote, booking, newsletter and lead forms, where the cost of a lost real message is high and the attacker is a generic bot.
Origin
Form Shield is extracted from the contact and booking forms of a production hosting platform, where it replaced a CAPTCHA proposal. The design decisions are written up in docs/how-it-works.md.
Changelog
See CHANGELOG.md.
Contributing
See Code of Conduct. New signals are especially welcome; please include precision numbers from real traffic if you have them.
Security
Please report vulnerabilities privately. See SECURITY.md.
Credits
License
MIT. See LICENSE.md.