m6web/firewall
A Symfony bundle that adds a configurable application-level firewall to PHP apps. Define rules to block malicious requests by IP, user agent, or custom conditions, with logging and ban handling to help protect your application from abuse.
m6web/firewall is a small, framework-agnostic library that maps client IP addresses (single IPs, CIDR blocks, and ranges) to named roles. It is archived and deprecated — the last release was October 2019 — so treat it as a legacy dependency to evaluate, not a default choice. Pin an exact version in composer.json, and do not expect PHP 8.4 or modern security fixes.
Minimal steps:
composer require m6web/firewall:^1.0
Firewall class first — that is the whole package. The flow is: define IP rules → set the client IP → resolve a role. Everything else is value objects around IP/CIDR parsing.use M6Web\Component\Firewall\Firewall;
$firewall = new Firewall();
$firewall->setIpRules([
'10.0.0.0/8' => 'internal',
'192.168.1.0/24' => 'internal',
'203.0.113.10' => 'office',
]);
$firewall->setDefaultRole('public');
$firewall->setIp($request->ip());
$role = $firewall->getRole(); // "internal", "office", or "public"
In Laravel there is no service provider, facade, or auto-discovery. You either instantiate it inside a middleware or bind a configured instance in a custom ServiceProvider.
Wrap it in middleware. This is how you use it day-to-day instead of calling it from controllers:
class ResolveIpRole
{
public function handle(Request $request, Closure $next)
{
$firewall = app(Firewall::class);
$firewall->setIp($request->ip());
$request->attributes->set('ip_role', $firewall->getRole());
return $next($request);
}
}
Bind a configured instance in a provider so rules live in config/firewall.php:
$this->app->singleton(Firewall::class, function () {
$firewall = new Firewall();
$firewall->setIpRules(config('firewall.rules', []));
$firewall->setDefaultRole(config('firewall.default_role', 'public'));
return $firewall;
});
Keep the config file free of env() calls so config:cache behaves correctly; use plain arrays there and read env values in the config file only.
Gate admin panels by role. Rather than scattering in_array($ip, ...) checks, expose the resolved role to Laravel's authorization layer:
Gate::define('access-admin', fn (User $user) =>
in_array(request()->attributes->get('ip_role'), ['internal', 'office'], true)
);
Protect operational routes. Apply the middleware to /telescope, /horizon, health endpoints, and feature-flagged routes. This is the package's strongest fit: a declarative allowlist that is easier to audit than inline conditionals.
Combine with auth, not instead of it. IP role is a coarse signal. Use it as an additional condition on top of authentication, never as the sole check for sensitive actions.
Test the role mapping in isolation. The library is pure PHP, so unit-test rule resolution directly without booting Laravel:
$firewall = (new Firewall())->setIpRules(['10.0.0.0/8' => 'internal'])
->setDefaultRole('public')
->setIp('10.1.2.3');
$this->assertSame('internal', $firewall->getRole());
Include boundary cases: /32, /0, IPv6, and addresses that are one octet outside a CIDR.
Symfony\Component\HttpFoundation\IpUtils::checkIp() (already a transitive dependency) or a maintained Laravel package. Audit the source before shipping.$request->ip() returns the socket IP unless TrustProxies is configured. Behind a load balancer every client resolves to the balancer. Fix TrustProxies first; do not manually trust X-Forwarded-For, since clients can spoof it.::ffff:10.0.0.1), IPv6 CIDRs, and leading/trailing whitespace in rules are common sources of silent mismatches. Normalize client IPs and test IPv6 if you enable it.if ($firewall->getRole()) — the default role is truthy and will make everything pass. Compare against an explicit allowlist.Firewall class if you need custom parsing, but the small surface area means any customization you need (database-backed rules, per-tenant allowlists) is better implemented in your own middleware with the library used only for CIDR matching.How can I help you explore Laravel packages today?