14four/laravel-force-host
Middleware for Laravel that forces a single canonical host for all requests. Automatically redirects to your configured domain (and optionally scheme), preventing duplicate URLs across www/non-www and improving SEO, cache consistency, and security.
Installation Add the package via Composer:
composer require 14four/laravel-force-host
Publish the config file (if needed):
php artisan vendor:publish --provider="14four\ForceHost\ForceHostServiceProvider"
Basic Configuration
Edit config/force-host.php to define allowed hosts:
'hosts' => [
'app.example.com',
'*.example.com', // Supports wildcards
],
First Use Case
Enable the middleware in app/Http/Kernel.php:
protected $middleware = [
// ...
\14four\ForceHost\ForceHostMiddleware::class,
];
Now, any request with a Host header not matching the allowed list will be rejected with a 403 Forbidden.
$middleware in Kernel.php to enforce host validation across all routes.Route::get('/admin', function () {
// ...
})->middleware(\14four\ForceHost\ForceHostMiddleware::class);
use 14four\ForceHost\ForceHostMiddleware;
$middleware = new ForceHostMiddleware(app('validator'), function ($host) {
return str_contains($host, 'internal.');
});
*.example.com to allow subdomains. Wildcards are resolved via regex:
'hosts' => [
'*.staging.example.com',
],
trusted_proxies in app/config/trustedproxies.php includes the proxy IPs. The package respects Laravel’s proxy detection.*.example.com) are case-sensitive in regex. Test edge cases like Example.COM.X-Forwarded-Host is trusted. Misconfiguration may bypass validation.Kernel.php for local testing:
if (app()->environment('local')) {
$middleware = [];
}
config/force-host.php:
'debug' => env('FORCE_HOST_DEBUG', false),
Logs rejected hosts to storage/logs/laravel.log.403 response by binding a closure to the middleware:
$middleware = new ForceHostMiddleware(app('validator'), null, function () {
return response()->json(['error' => 'Invalid host'], 403);
});
$validator = app('validator');
$validator->extend('host_allowed', function ($attribute, $value, $parameters) {
return in_array($value, ['app.example.com', 'api.example.com']);
});
forcehost.rejected events to log or alert on blocked requests:
Event::listen('forcehost.rejected', function ($request, $host) {
Log::warning("Blocked host: {$host} from IP: {$request->ip()}");
});
public function boot()
{
$this->app->singleton('forcehost.patterns', function () {
return collect(config('force-host.hosts'))
->map(fn ($host) => preg_quote($host, '/'))
->map(fn ($host) => str_replace('\*', '.*', $host))
->map(fn ($host) => "/^{$host}$/i")
->values();
});
}
How can I help you explore Laravel packages today?