Banhammer 🔨
A simple and powerful ban package for Laravel - ban models, IPs, and countries with ease.
Banhammer allows you to ban any Eloquent model, IP addresses, and even entire countries. Bans can be permanent or temporary with automatic expiration.
📋 Table of Contents
🚀 Quick Start
composer require mchev/banhammer
php artisan vendor:publish --provider="Mchev\Banhammer\BanhammerServiceProvider" --tag="migrations"
php artisan migrateAdd the trait to your model:
use Mchev\Banhammer\Traits\Bannable;
class User extends Model
{
use Bannable;
}Ban a user:
$user->ban();✨ Features
- ✅ Ban any Eloquent model (User, Team, etc.)
- ✅ Ban IP addresses (single or multiple)
- ✅ Block entire countries
- ✅ Temporary or permanent bans
- ✅ Automatic expiration handling
- ✅ Middleware protection
- ✅ Event system
- ✅ Metadata support
- ✅ Laravel 9, 10, 11 & 12 compatible
📦 Installation
Requirements
- PHP 8.0+
- Laravel 9, 10, 11 or 12
Setup
-
Install the package:
composer require mchev/banhammer
-
Publish and run migrations:
php artisan vendor:publish --provider="Mchev\Banhammer\BanhammerServiceProvider" --tag="migrations" php artisan migrate
-
Publish config (optional):
php artisan vendor:publish --provider="Mchev\Banhammer\BanhammerServiceProvider" --tag="config"
💡 The config file allows you to customize table name, model, fallback URLs, and more.
📖 Usage Guide
Banning Models
Make a Model Bannable
Add the Bannable trait to any model:
use Mchev\Banhammer\Traits\Bannable;
class User extends Model
{
use Bannable;
}💡 You can add the trait to multiple models (User, Team, Group, etc.)
Basic Operations
| Action | Code |
|---|---|
| Ban a user | $user->ban() |
| Ban with expiration | $user->banUntil('2 days') |
| Check if banned | $user->isBanned() |
| Check if not banned | $user->isNotBanned() |
| Unban | $user->unban() |
Advanced Ban Options
$user->ban([
'comment' => "You've been evil",
'ip' => "8.8.8.8",
'expired_at' => Carbon::now()->addDays(7),
'created_by_type' => 'App\Models\Admin',
'created_by_id' => auth()->id(),
'metas' => [
'route' => request()->route()->getName(),
'user_agent' => request()->header('user-agent')
]
]);
⚠️ Withoutexpired_at, the ban is permanent.
Query Scopes
// Get banned users
$bannedUsers = User::banned()->get();
// Get non-banned users
$activeUsers = User::notBanned()->get();
// Alternative syntax
$activeUsers = User::banned(false)->get();List Bans
// All bans for a model
$bans = $user->bans()->get();
// Only expired bans
$expired = $user->bans()->expired()->get();
// Active bans
$active = $user->bans()->notExpired()->get();Banning IPs
Basic Operations
use Mchev\Banhammer\IP;
// Ban single IP
IP::ban("8.8.8.8");
// Ban multiple IPs
IP::ban(["8.8.8.8", "4.4.4.4", "1.1.1.1"]);
// Ban with expiration
IP::ban("8.8.8.8", [], now()->addMinutes(10));
// Ban with metadata
IP::ban("8.8.8.8", [
'reason' => 'spam',
'severity' => 'high'
]);Unban IPs
// Unban single IP
IP::unban("8.8.8.8");
// Unban multiple IPs
IP::unban(["8.8.8.8", "4.4.4.4"]);Check & List Banned IPs
// Check if IP is banned
IP::isBanned("8.8.8.8"); // bool
// Get all banned IPs
$ips = IP::banned()->get(); // Collection
$ips = IP::banned()->pluck('ip')->toArray(); // ArrayCountry Blocking
Block access from specific countries automatically.
Configuration
-
Enable country blocking in
config/ban.php:'block_by_country' => true, -
Specify blocked countries:
'blocked_countries' => ['FR', 'ES', 'US'],
That's it! The middleware will automatically block requests from these countries.
⚠️ Rate Limit Notice: The free version of ip-api.com has a limit of 45 requests/minute. Exceeding this will result in 429 errors until the limit resets.
💡 Want to improve this? If you have suggestions for better geolocation services or want to contribute improvements, please open an issue or submit a pull request.
Middleware
Protect your routes with ban middleware:
Available Middleware
| Middleware | Description |
|---|---|
auth.banned |
Blocks banned users |
ip.banned |
Blocks banned IPs |
logout.banned |
Logs out and blocks banned users/IPs |
Usage
// Single route
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware('auth.banned');
// Route group
Route::middleware(['auth.banned'])->group(function () {
Route::get('/profile', [ProfileController::class, 'index']);
Route::get('/settings', [SettingsController::class, 'index']);
});
// Block IPs on all routes
// Add to app/Http/Kernel.php:
protected $middleware = [
// ...
\Mchev\Banhammer\Middleware\IPBanned::class,
];💡 Tip:
logout.bannedincludes the functionality of bothauth.bannedandip.banned, so you don't need to use them together.
Scheduler
Banhammer automatically deletes expired bans using Laravel's scheduler.
Setup
⚠️ Important: You must have a cron job running Laravel's scheduler:* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
Configuration
By default, the banhammer:unban command runs every minute. You can customize this:
Disable automatic scheduler:
// config/ban.php
'scheduler_enabled' => false,Or via environment:
BANHAMMER_SCHEDULER_ENABLED=falseChange frequency:
// config/ban.php
'scheduler_periodicity' => 'everyFiveMinutes', // or 'hourly', 'daily', etc.Or via environment:
BANHAMMER_SCHEDULER_PERIODICITY=everyFiveMinutesAvailable frequencies: everyMinute, everyFiveMinutes, everyTenMinutes, everyFifteenMinutes, everyThirtyMinutes, hourly, daily, twiceDaily, etc.
Events
Listen to ban/unban events:
use Mchev\Banhammer\Events\ModelWasBanned;
use Mchev\Banhammer\Events\ModelWasUnbanned;
Event::listen(ModelWasBanned::class, function ($event) {
// User was banned
Log::info("User {$event->ban->bannable->id} was banned");
});
Event::listen(ModelWasUnbanned::class, function ($event) {
// User was unbanned
Log::info("User {$event->ban->bannable->id} was unbanned");
});🔧 Advanced Topics
Metas
Store additional data with bans:
// Set meta
$ban->setMeta('username', 'Jane');
$ban->setMeta('reason', 'spam');
// Get meta
$ban->getMeta('username'); // 'Jane'
// Check if meta exists
$ban->hasMeta('username'); // true
// Remove meta
$ban->forgetMeta('username');Filter by Meta
// Find bans with specific meta
IP::banned()->whereMeta('username', 'Jane')->get();
$user->bans()->whereMeta('reason', 'spam')->get();
User::whereBansMeta('username', 'Jane')->get();Ban with Metas
// When banning
$user->ban([
'metas' => [
'route' => request()->route()->getName(),
'user_agent' => request()->header('user-agent')
]
]);
IP::ban("8.8.8.8", [
'reason' => 'spam',
'severity' => 'high'
]);UUIDs
To use UUIDs instead of auto-incrementing IDs:
-
Publish migrations:
php artisan vendor:publish --provider="Mchev\Banhammer\BanhammerServiceProvider" --tag="migrations"
-
Edit the migration:
- $table->id(); + $table->uuid('id');
-
Create a custom Ban model:
namespace App\Models; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Mchev\Banhammer\Models\Ban as BanhammerBan; class Ban extends BanhammerBan { use HasUuids; }
-
Update config:
// config/ban.php 'model' => \App\Models\Ban::class,
Upgrading To 2.0 from 1.x
-
Update composer.json:
"require": { "mchev/banhammer": "^2.0" }
-
Update the package:
composer update mchev/banhammer
-
Update configuration:
# Backup your current config cp config/ban.php config/ban.php.backup # Republish config php artisan vendor:publish --provider="Mchev\Banhammer\BanhammerServiceProvider" --tag="config" --force # Review and merge any custom settings
🛠️ Development
Commands
# Manually delete expired bans
php artisan banhammer:unban
# Permanently delete all expired bans
php artisan banhammer:clearProgrammatic Usage
use Mchev\Banhammer\Banhammer;
// Delete expired bans
Banhammer::unbanExpired();
// Permanently delete expired bans
Banhammer::clear();Testing
composer test🤝 Contributing
We welcome contributions! Please:
- Open issues for bug reports or feature requests
- Submit pull requests (mark as "ready for review")
- Ensure all tests pass
- Follow Laravel coding standards
💡 Pull requests in "draft" state will be closed after a few days of inactivity.
🙏 Credits
Inspired by laravel-ban from cybercog.
📄 License
The MIT License (MIT). Please see License File for more information.