Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lockout Laravel Package

rappasoft/lockout

Laravel Lockout is a simple package that locks user accounts after a configurable number of failed login attempts, helping protect applications from brute-force attacks. It includes middleware, events, and customizable lockout behavior for Laravel authentication.

View on GitHub
Deep Wiki
Context7

Getting Started

Lockout is the small package you reach for when a non-production app (staging, UAT, a demo box) needs to be reachable only from a known set of IPs without standing up a VPN. It wraps your app in middleware, compares the request IP against a whitelist, and lets you flip protection on and off per environment without redeploying code.

  1. composer require rappasoft/lockout (v6.1.0 supports Laravel 11/12 on PHP 8.2+, Laravel 13 on PHP 8.3+; tested through PHP 8.5).
  2. Publish the config and the bundled maintenance view so they live in your repo: php artisan vendor:publish --tag=lockout-config and the view tag listed by the service provider (v6.1.0 fixed bundled view loading/publishing, so this should work cleanly now).
  3. Register the package middleware on the web group (or the groups you want protected) in bootstrap/app.php for Laravel 11+.
  4. Seed config/lockout.php with your allow-list — office egress IPs, VPN ranges, and CI runner CIDRs.
  5. Toggle it with the enable/disable artisan commands against your staging environment and confirm with curl from an allowed and a disallowed address.

First use case to wire up: lock the staging site to the office IP plus your CI provider's published CIDR blocks, so deploys and smoke tests keep working while the rest of the internet gets the maintenance view.

Implementation Patterns

Environment-scoped configuration. Keep the whitelist in config/lockout.php reading from env vars (LOCKOUT_IPS, etc.) so each environment carries its own list. Local/testing should be excluded so php artisan test and Tinker never hit the gate.

Deployment pipeline integration. The realistic flow is: deploy → run smoke tests → enable lockout. Use the enable command at the end of the deploy script and disable it at the start of a release window. Since v6.1.0 the commands update the active environment file atomically and preserve its existing file permissions, so you no longer need a chmod/chown fix-up step in the script — but you do still need to rebuild config caches and restart long-running processes (see Gotchas).

Route-level targeting. Rather than protecting the whole app, you can scope the middleware to a group: internal admin panels, /horizon, /telescope, preview routes. v6.1.0 fixes literal route pattern matching and detects named/API routes before global middleware dispatch, so patterns registered by name behave as expected instead of silently falling through.

IPv6-aware allow-lists. With v6.1.0 you can finally write 2001:db8::/32 or ::1 alongside IPv4 entries. Pattern: whitelist both loopback forms (127.0.0.1 and ::1) in local config, and list IPv6 CIDRs for cloud egress that is v6-only — otherwise the app "works from the office" but fails from a v6-fronted CI runner.

Custom maintenance view. Publish the view and brand it ("Staging — request access at #eng-help"). Keep the published copy in version control; don't rely on the vendor path, which is what the v6.1.0 view-loading fix addresses.

Testing. In feature tests, disable the middleware (or point the config at an empty/* allow-list) rather than spoofing REMOTE_ADDR. If you do want to assert lockout behaviour, $this->withServerVariables(['REMOTE_ADDR' => '203.0.113.9']) is the cleanest hook.

Gotchas and Tips

Config cache is the number one footgun. After enable/disable, run php artisan config:cache (or config:clear) as part of the same step. --clear-cache is still accepted on the commands for existing scripts, but don't assume it covers every cached artifact — verify with php artisan about in the target environment.

Restart Octane and workers. Long-running processes hold the old environment/config in memory. php artisan octane:reload (and a queue worker restart) is mandatory after toggling, or you'll get a half-locked app where web requests are blocked and queued jobs are not.

APP_READ_ONLY from an external env source. If the variable comes from Kubernetes/ECS/Heroku/Forge env management rather than the on-disk .env, the commands cannot persist it — the release notes are explicit that you must change it through your deployment tooling. Treat the artisan commands as a local/staging convenience, not a production control plane.

Fail-closed behaviour and exit codes. The commands now fail without emitting success events when persistence fails (unwritable .env, read-only filesystem). That's an improvement, but it means a &&-chained deploy script can silently skip the rest — check the exit status explicitly and log it.

Proxies will defeat your allow-list. Behind Cloudflare, an ALB, or a reverse proxy, the request IP is the proxy's. Configure TrustProxies correctly, or you'll either lock everyone out or let everyone in. v6.1.0's improved IP handling doesn't change this — it's an application-level trust decision.

Validate CIDRs yourself too. Malformed CIDR handling is now fixed (no more crashes on a stray entry), but a silently-ignored bad entry means an accidentally public app. Add a config test that asserts every entry parses as an IP or CIDR.

Version and platform notes. Laravel 11 is outside security support — plan on Laravel 12 or 13 in production. Laravel 13 requires PHP 8.3+. CI on Laravel 11 + PHP 8.5 emits two upstream Testbench deprecations that are not yours to fix; don't chase them.

Upgrading from v6.0.0. No breaking changes signalled. Republish the maintenance view if you were working around the loading bug, re-run your config assertions if you rely on route patterns (literal matching changed), and re-test any IPv6 entries you'd previously stripped out.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource