IP Whitelisting for Laravel Admin Panels
Your admin panel is the keys to the kingdom. User management, data exports, configuration changes - everything sensitive lives there. Even with strong authentication, exposing it to the entire internet is unnecessary risk.
IP whitelisting adds a layer of defense. Only requests from known IP addresses reach your admin routes. Everyone else gets a 403 before they can even attempt a login.
The pragmarx/firewall package makes this easy in Laravel.
Installation
composer require pragmarx/firewall
Publish the configuration:
php artisan vendor:publish --provider="PragmaRX\Firewall\Vendor\Laravel\ServiceProvider"
Configuration
Edit config/firewall.php:
return [
'enabled' => env('FIREWALL_ENABLED', true),
// IPs that are blocked
'blacklist' => [],
// IPs that are allowed (when whitelist mode is enabled)
'whitelist' => [
'<office-ip>', // Office IP
'<ceo-home-ip>', // CEO home
'<vpn-exit-ip>', // VPN exit
'<remote-employee-ip>', // Remote employee
],
'responses' => [
'whitelist' => [
'code' => 403,
'message' => null,
'view' => null,
'redirect_to' => null,
'abort' => false,
],
],
// CIDR range support
'enable_range_search' => true,
// Country-based blocking (requires GeoIP database)
'enable_country_search' => false,
// Cache settings
'cache_expire_time' => 60,
// Rate limiting for attack detection
'attack_blocker' => [
'enabled' => [
'ip' => true,
'country' => false,
],
'allowed_frequency' => [
'ip' => [
'requests' => 50,
'seconds' => 60,
],
],
],
];
Applying to Routes
Create a route group for admin routes:
// routes/web.php
Route::middleware(['auth', 'firewall.whitelist'])
->prefix('admin')
->group(function () {
Route::get('/', [AdminController::class, 'index']);
Route::resource('users', UserAdminController::class);
Route::resource('settings', SettingsController::class);
});
The firewall.whitelist middleware checks the request IP against your whitelist. If it's not listed, the request is rejected immediately.
For Filament Admin Panels
If you're using Filament, add the middleware to its configuration:
// config/filament.php
'middleware' => [
'auth' => [
Authenticate::class,
],
'base' => [
// ... existing middleware
\PragmaRX\Firewall\Middleware\FirewallWhitelist::class,
],
],
Or in Filament 3, in a panel provider:
public function panel(Panel $panel): Panel
{
return $panel
->default()
->middleware([
\PragmaRX\Firewall\Middleware\FirewallWhitelist::class,
]);
}
Environment-Based Configuration
Don't whitelist in development:
'enabled' => env('FIREWALL_ENABLED', true),
# .env.local
FIREWALL_ENABLED=false
# .env.production
FIREWALL_ENABLED=true
Or whitelist localhost for development:
'whitelist' => [
'127.0.0.1',
'::1', // IPv6 localhost
// Production IPs...
],
Managing IPs
For small teams, editing the config file works. For larger organizations, consider:
Database storage:
// Enable database mode
'use_database' => true,
Then manage IPs via Artisan:
php artisan firewall:whitelist <remote-employee-ip>
php artisan firewall:remove <remote-employee-ip>
php artisan firewall:list
Admin interface:
Build a simple CRUD for managing allowed IPs. Store them in a database table and load them in the config:
'whitelist' => cache()->remember('firewall.whitelist', 3600, function () {
return AllowedIp::pluck('ip')->toArray();
}),
CIDR Ranges
Allow entire subnets with CIDR notation:
'whitelist' => [
'<office-network>/24', // Office network
'10.0.0.0/8', // Internal private network
],
Useful for office networks where individual IPs vary.
Logging and Monitoring
Enable logging to track blocked requests:
'enable_log' => true,
'log_stack' => 'security', // Custom log channel
Create a dedicated log channel:
// config/logging.php
'channels' => [
'security' => [
'driver' => 'daily',
'path' => storage_path('logs/security.log'),
'days' => 30,
],
],
Review logs for:
- Unexpected blocked requests (legitimate users from new IPs)
- Attack patterns (repeated attempts from unknown IPs)
- Configuration issues (whitelisted IPs still being blocked)
Notifications
Get alerted when attacks are detected:
'notifications' => [
'enabled' => true,
'users' => [
'emails' => [
'security@example.com',
],
],
'channels' => [
'slack' => ['enabled' => true],
'mail' => ['enabled' => true],
],
],
Handling Dynamic IPs
If team members have dynamic home IPs, options include:
VPN. Everyone connects through a VPN with a static exit IP. Whitelist that one IP.
Self-service updates. Build a page (outside the firewall) where authenticated users can register their current IP:
Route::middleware(['auth'])->get('/register-ip', function () {
$ip = request()->ip();
AllowedIp::updateOrCreate(
['user_id' => auth()->id()],
['ip' => $ip, 'expires_at' => now()->addDays(7)]
);
cache()->forget('firewall.whitelist');
return redirect('/admin');
});
Time-limited access. Store IPs with expiration and clean up old ones daily.
Defense in Depth
IP whitelisting is one layer. Combine it with:
- Strong authentication (2FA, Passkeys)
- Rate limiting on login endpoints
- Audit logging of admin actions
- Least privilege roles and permissions
- Separate admin subdomain for additional isolation
No single measure is bulletproof. Layers of defense mean attackers must bypass multiple protections.
Conclusion
IP whitelisting dramatically reduces your admin panel's attack surface. Instead of defending against the entire internet, you're defending against known, trusted networks.
The pragmarx/firewall package makes implementation straightforward - configure your IPs, apply the middleware, and unauthorized requests never reach your application code.
For internal tools and admin panels, it's one of the simplest security wins available.