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

Firewall Laravel Package

pragmarx/firewall

A Laravel firewall package for managing IP blacklists and whitelists. It provides middleware, database-backed rules, and Artisan commands to block or allow requests by IP address (and optionally country), helping protect your app from unwanted traffic.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and let Laravel's package discovery register the service provider (Laravel 5.5+); on older versions add PragmaRX\Firewall\Vendor\Laravel\ServiceProvider to config/app.php and the Firewall facade to the aliases array.

composer require pragmarx/firewall
php artisan vendor:publish --provider="PragmaRX\Firewall\Vendor\Laravel\ServiceProvider"
php artisan migrate

Publishing gives you two things to look at first: config/firewall.php (static whitelist/blacklist entries, response code, GeoIP settings) and the firewall_ips / country migrations. Read the config file top-to-bottom before wiring anything up — that is where the package's mental model lives.

Register the middleware in app/Http/Kernel.php:

protected $routeMiddleware = [
    // ...
    'firewall' => \PragmaRX\Firewall\Middleware\Firewall::class,
];

First use case: protecting an admin panel. Whitelist your office/VPN address from the CLI, then attach firewall to the admin route group:

php artisan firewall:whitelist 203.0.113.10
php artisan firewall:list
Route::middleware(['web', 'firewall'])->prefix('admin')->group(function () {
    // ...
});

Implementation Patterns

Static vs. database lists. The config arrays are for addresses that ship with your code (CI runners, monitoring, corporate egress). The firewall_ips table is the operational store — populated at runtime by facade calls or Artisan. Keep permanent infrastructure in config and everything else in the DB, otherwise every allow/deny becomes a deploy.

Middleware placement. The firewall middleware is meant to wrap route groups, not sit globally on web. Applying it to everything means one bad blacklist row takes down the whole site, including the login page you need to fix it. Common patterns:

  • A firewall group on /admin, /api, and anything internal.
  • A separate firewall:strict-style group that also checks country codes for geo-restricted endpoints.
  • Explicit exclusions for /health, /webhooks/*, and OAuth callbacks — these come from third-party IPs and should never be filtered.

Programmatic use via the facade. For application-driven rules (blocking an IP after N failed logins, whitelisting a customer's office range from a settings screen):

Firewall::blacklist($ip);
Firewall::whitelist($ip);
Firewall::isBlacklisted($ip);   // returns bool
Firewall::isWhitelisted($ip);   // returns bool

This is the extension point most teams actually use — a listener on Illuminate\Auth\Events\Failed that blacklists after a burst of attempts is a few lines and needs no cron.

Operational commands. firewall:blacklist, firewall:whitelist, firewall:remove, firewall:list, and firewall:clear are the day-to-day admin surface. Wrap them in your own Artisan command or an internal admin screen if non-engineers need to manage access, and make sure the audit trail lives somewhere (the table has timestamps, but not who did it).

Geo-blocking. Country rules go through the package's GeoIP support and live in their own table. This is genuinely useful for blocking regions from signup or checkout flows, but it is a slow, best-effort lookup — treat it as a coarse filter layered on top of the IP logic, not a security boundary.

Integration with auth. Because the middleware runs before your controllers, a blacklisted user never reaches their session. If you need "blocked users see an explanation page," catch the resulting 403 in app/Exceptions/Handler.php and render a friendly response there rather than trying to redirect from inside the package.

Gotchas and Tips

Real client IP is the #1 failure mode. If the app sits behind a load balancer, CDN, or Cloudflare, $request->ip() returns the proxy address. You will either whitelist everything or blacklist everyone. Configure TrustProxies ($proxies, HEADER_X_FORWARDED_FOR) before the firewall middleware in the stack, and verify by logging $request->ip() from a test route.

Middleware ordering matters in both directions. The firewall should run after TrustProxies (so it sees the real IP) and before anything that mutates the request. In the web group, place it high; in api, before auth so blocked clients don't hit the database for token lookups.

Whitelist wins over blacklist. A whitelisted IP is never blocked, even if it is also in the blacklist table. This is intentional (it is your break-glass mechanism) but surprising when someone adds an IP to the blacklist and the entry appears to do nothing. Teach the team: whitelist first, blacklist second, and clean up stale whitelists.

Self-lockout. Every team using this eventually blacklists the office IP or the NAT gateway. Keep a break-glass path: a console command run directly on the server, an SSH tunnel, or a config-level whitelist that bypasses the DB. Do this before it happens, not after.

A DB hit per request. The middleware resolves IP state from the firewall_ips table on every incoming request. Ensure the IP column is indexed, and avoid adding this middleware to high-volume public endpoints where a cheap config check or a CDN/WAF rule would be more appropriate. Firewall rules belong at the edge where possible.

Config caching is fine, but the config is static. php artisan config:cache works because the package only reads config at boot, but it means editing config/firewall.php on production does nothing until you re-cache. Use the DB table and commands for anything operational.

GeoIP is the fragile part. Whether you use a local database or an API, lookups can fail or time out, and behaviour on failure is a policy decision. Test what happens when the lookup returns nothing — do you fail open (allow) or closed (deny)? Also verify a test IP actually resolves to a country before relying on it in staging; many private ranges return nothing.

IPv6. The package handles IPv6, but your ops documentation probably does not. When someone reports "the block didn't work," confirm they typed the address the way the client actually connects — ::1 not 127.0.0.1, and no CIDR notation unless explicitly supported.

Extension points. The model, table names, and middleware are all configurable/publishable. If you need custom semantics (blocking by user ID, per-tenant rules, different response codes per route), publish the middleware and subclass it rather than fighting the built-in behaviour. The facade is the stable API; the internals are not something to reach into.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route