vcian/laravel-ip-gateway
Laravel IP Gateway adds middleware and configuration for restricting access by IP address. Easily allow or deny requests, protect routes, and manage whitelists or blacklists with a simple Laravel-friendly setup.
composer require vcian/laravel-ip-gateway
ip-gateway; if unsure, run php artisan vendor:publish and pick the vcian/laravel-ip-gateway provider):
php artisan vendor:publish --tag=ip-gateway
config/ip-gateway.php first. That file is the whole package in practice — it holds the allow/block lists and the enable switch. Read it before touching any code; almost every behaviour you care about is a string array there.app/Http/Kernel.php:
protected $routeMiddleware = [
// ...
'ip.gateway' => \Vcian\IpGateway\Middleware\IpGatewayMiddleware::class,
];
On Laravel 11+:
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->alias(['ip.gateway' => \Vcian\IpGateway\Middleware\IpGatewayMiddleware::class]);
})
Verify the exact class name from vendor/vcian/laravel-ip-gateway/src/ — the namespace casing is the usual source of "class not found" after install.Route::middleware(['web', 'ip.gateway'])->group(function () {
Route::get('/admin', [AdminController::class, 'index']);
});
Put your own IP in the allow list before enabling it anywhere global, or you will lock yourself out on the first request.allowed_ips with office/VPN ranges and apply the middleware to the entire admin panel or a staging route group. This is the package's strongest fit: a cheap, request-time gate that runs before your controllers and (if ordered correctly) before expensive auth/DB work.allowed_ips empty and list blocked_ips for repeat offenders or scrapers. Treat this as a stopgap — it is not a substitute for throttle or a WAF, because blocked IPs rotate.env() so staging is restricted and local is open:
'allowed_ips' => array_filter(explode(',', env('IP_GATEWAY_ALLOWED', ''))),
Remember config:cache after changing .env — see gotchas.web or api stack) is the blunt instrument for "maintenance mode / internal-only deployment". Route-scoped aliases are the default for per-area restrictions.auth so an allowlisted-but-unauthenticated request still hits your login flow:
Route::middleware(['ip.gateway', 'auth'])->group(...);
$this->withServerVariables(['REMOTE_ADDR' => '203.0.113.10'])
->get('/admin')
->assertForbidden();
Write one test per allowed IP, one per blocked IP, and one for the empty-list case — the last one is where most misconfigurations hide.Accept: application/json-aware custom middleware so clients get a proper {"message": "Forbidden"} payload.request()->ip() behind a load balancer. This is the number-one failure. Behind Cloudflare/ALB/nginx, the resolved IP is the proxy's, so every request looks identical and either everyone or no one is allowed. Configure TrustProxies with the correct X-Forwarded-For handling before evaluating this package.::1 and 127.0.0.1 are different strings. Local testing on localhost often resolves to ::1 and silently fails an allowlist containing only 127.0.0.1. Add both..env, run php artisan config:clear (dev) or config:cache (prod). A stale cached config is the second most common "it's not working" cause.TrustProxies and before auth/bindings so rejected requests are cheap and the IP is already resolved. Ordering it before TrustProxies makes it read the proxy IP.IP_GATEWAY_ENABLED switch so a bad list can be disabled without a code change.192.168.1.0/24 and 192.168.1.* explicitly; if unsupported, do range checks in a wrapping middleware rather than forking.Kernel.php instructions in many READMEs don't apply. If the alias isn't registered in bootstrap/app.php, you get "Target class [ip.gateway] does not exist".composer.json and keep the integration behind your own middleware/facade so swapping in a hand-rolled in_array($request->ip(), config('...')) check is a 10-line change if the package goes stale.RestrictToAllowedIps middleware that delegates to the package's logic (or replicates its list check), giving you a place to add logging, JSON responses, and CIDR handling without modifying vendor code.How can I help you explore Laravel packages today?