mchev / banhammer
Banhammer for Laravel allows you to ban any Model by key and by IP.
Fund package maintenance!
mchev
Installs: 81 453
Dependents: 2
Suggesters: 0
Security: 0
Stars: 362
Watchers: 2
Forks: 29
Open Issues: 0
pkg:composer/mchev/banhammer
Requires
- php: ^8.0
Requires (Dev)
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0
- phpunit/phpunit: ^9.6|^10.5|^11.5.3
README
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 migrate
Add 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') ] ]);
⚠️ Without
expired_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(); // Array
Country 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=false
Change frequency:
// config/ban.php 'scheduler_periodicity' => 'everyFiveMinutes', // or 'hourly', 'daily', etc.
Or via environment:
BANHAMMER_SCHEDULER_PERIODICITY=everyFiveMinutes
Available 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:clear
Programmatic 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.