
Adding IP Whitelisting to Your Filament PHP Admin Panel
TLDR;
Want to restrict access to your Filament admin panel to only specific IPs? Here's the coles notes:
- Create a new middleware to check if the request IP is in your allowed list
- Set up an
allowed_ips
database table to manage the whitelist - Add the middleware to your Filament
AdminPanelProvider
config - Done. Now only whitelisted IPs can access your Filament panel
This approach lets you manage allowed IPs from the database instead of hardcoding them in config files or the env settings.
Why IP Whitelisting?
Sometimes, just protecting your admin panel with a password doesn't feel like enough. You might want to limit access to your home IP address for small apps, prevent brute force attempts altogether, or simply add another layer of protection on top of authentication. This is where IP whitelisting comes in.
Step 1: Create the Middleware
We'll start with a custom middleware that checks if the request IP is in a database table. Here's what it looks like:
// app/Http/Middleware/IPWhitelist.php
public function handle(Request $request, Closure $next): Response
{
$allowedIps = AllowedIP::all()->pluck('ip_address')->toArray();
if (!in_array($request->ip(), $allowedIps)) {
if ($request->expectsJson()) {
return response()->json(['error' => 'The request is unauthorized'], 401);
}
abort(401, 'The request is unauthorized');
}
return $next($request);
}
Nothing fancy. It grabs all allowed IPs from the database, and checks if the incoming request matches.
Step 2: Create the AllowedIP Model and Migration
Let’s store allowed IPs in a proper Eloquent model.
// app/Models/AllowedIP.php
class AllowedIP extends Model
{
protected $table = 'allowed_ips';
protected $fillable = [
'ip_address',
'description',
];
}
And the corresponding migration:
// database/migrations/xxxx_xx_xx_create_allowed_ips_table.php
Schema::create('allowed_ips', function (Blueprint $table) {
$table->id();
$table->string('ip_address')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
Step 3: Seed Some IPs
For local testing, let’s seed 127.0.0.1
.
// database/seeders/AllowedIPSeeder.php
AllowedIP::firstOrCreate([
'ip_address' => '127.0.0.1',
], [
'description' => 'Localhost IP',
]);
And don’t forget to call it from your DatabaseSeeder.php
:
$this->call(AllowedIPSeeder::class);
Step 4: Register the Middleware in Filament
The last piece is telling Filament to use this middleware for the admin panel. Inside your AdminPanelProvider
you can add your new middleware to the top of the stack:
// app/Providers/Filament/AdminPanelProvider.php
use App\Http\Middleware\IPWhitelist;
// ...
->middleware([
IPWhitelist::class, // 👈 Add this
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
])
And that's it! The IP whitelist will now be checked on each request to your admin dashboard.
Step 5: Testing It Works
First let's run our database migrations and seeders so that we have some data to work with.
php artisan migrate --seed
You should now be able to fire up your web browser and check if the admin login page is accessible. Note that if your local IP address is different than 127.0.0.1
, something like 192.168.0.x
, then you can simply add this IP address to the whitelist instead. You can also clear the allowed_ips
table to ensure that you receive a 401 Unauthorized response code as expected.
We should also write a few quick tests to make sure everything’s working as expected.
// tests/Feature/IPWhitelistTest.php
protected function setUp(): void
{
parent::setUp();
AllowedIp::create([
'ip_address' => '123.123.123.123',
'description' => 'Valid IP',
]);
}
public function test_admin_panel_denies_access_from_invalid_ip(): void
{
collect([
'filament.admin.auth.login',
'filament.admin.pages.dashboard',
])->each(
fn ($route) =>
$this->withServerVariables([
'REMOTE_ADDR' => '111.111.111.111',
])->get(route($route))->assertUnauthorized()
);
}
public function test_admin_panel_allows_access_from_valid_ip(): void
{
// Login page should return 200 OK
$this->withServerVariables([
'REMOTE_ADDR' => '123.123.123.123',
])->get(route('filament.admin.auth.login'))->assertStatus(Response::HTTP_OK);
// Dashboard should redirect to login (302)
$this->withServerVariables([
'REMOTE_ADDR' => '123.123.123.123',
])->get(route('filament.admin.pages.dashboard'))
->assertStatus(Response::HTTP_FOUND)
->assertRedirectContains(route('filament.admin.auth.login'));
}
Step 6. (Bonus) Caching the Allowed IP Addresses
To improve performance, especially if your list of allowed IPs grows, it's a good idea to cache the results instead of querying the database on every request. You can cache the IP list for a short period (like 15 minutes) since running a query once per that timeframe isn't too taxing on the database. Just make sure to clear the cache anytime a new IP is added or removed from the whitelist.
// app/Http/Middleware/IPWhitelist.php
$allowedIps = Cache::remember('allowed_ips', now()->addMinutes(15), fn () => AllowedIP::all()->pluck('ip_address')->toArray());
// app/Observers/AllowedIPObserver.php
public function saved(AllowedIP $allowedIP): void
{
Cache::forget('allowed_ips');
}
Wrap-Up
And that’s all you need to IP-lock your Filament admin panel.
The best part? You can manage allowed IPs through your own UI later on. Add a Filament resource to CRUD them in the admin panel itself. Just don’t forget to give yourself access first!