elic-dev/laravel-site-protection
A Laravel package for adding configurable site-wide protection to your application. It uses middleware and Laravel-native configuration to restrict access, shield staging or private sites, and keep unwanted visitors out while authorized users continue smoothly. Simple setup and sensible defaults.
Install the package
composer require elic-dev/laravel-site-protection
Publish the config (and optionally the view)
Check the README for the exact publish tag/provider, but it will be one of:
php artisan vendor:publish --tag=site-protection-config
php artisan vendor:publish --tag=site-protection-views
# or, if no tags are defined:
php artisan vendor:publish --provider="ElicDev\SiteProtection\SiteProtectionServiceProvider"
Set the password via environment
The package is config-driven, so the password should come from .env rather than a committed config file:
SITE_PROTECTION_ENABLED=true
SITE_PROTECTION_PASSWORD=correct-horse-battery-staple
Register the middleware
For a whole-site gate, register it globally (Laravel 11+ bootstrap/app.php):
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\ElicDev\SiteProtection\Http\Middleware\ProtectSite::class);
})
Or, more commonly, apply it to a route group so you can carve out exceptions.
Where to look first
config/site-protection.php — enable flag, password, excluded paths, view name, cookie/session settings. This is the source of truth for how the package behaves.First use case
The canonical fit is a staging / preview / client-review environment: keep the site publicly reachable but behind a shared password so search engines, crawlers, and casual visitors can't see it. It is not a replacement for laravel/auth — it's a single shared gate, not per-user accounts.
Environment-scoped enabling. The cleanest pattern is to enable the gate only where it makes sense:
// config/site-protection.php
'enabled' => env('SITE_PROTECTION_ENABLED', app()->environment() !== 'production'),
Then set SITE_PROTECTION_ENABLED=true in staging/preview .env files and leave it false in local and production. Review apps (Forge, Vapor, Heroku review apps, Ploi) get a per-deploy password injected as an env var — a different password per preview environment is a cheap way to revoke access when a review is done.
Route-group gating instead of global middleware. If only part of the app needs protecting (e.g. an internal dashboard or a pre-launch marketing site), wrap the group:
Route::middleware('site-protection')->group(function () {
// protected routes
});
and alias the middleware in bootstrap/app.php / Kernel.php.
Exclusions are the main integration point. You will immediately need to exclude:
/up, /health).sitemap.xml / robots.txt if you want them reachable.Most packages of this shape expose an except array in config; prefer that over writing a custom middleware.
Re-skin the form. Publish the view and match it to your app's layout. Keep the form posting to the package's route with @csrf intact — the package handles the password check, you only need to own the markup.
Combine, don't replace, with auth. A common production pattern is site-protection on the whole site plus auth on admin routes. The site gate stops the public; auth still does per-user authorization. Don't try to bolt user accounts onto this package.
Testing. In phpunit.xml, set SITE_PROTECTION_ENABLED=false for the test suite, or call $this->withoutMiddleware(ProtectSite::class) in feature tests. For tests that exercise the gate itself, set the password in the test env and assert the redirect-to-form / successful-post flow explicitly.
Automation / smoke tests. If CI hits a staging URL, either disable protection for the CI IP range (if the package supports IP allowlists — check the config) or have the smoke test POST the password first and reuse the session cookie.
config:cache will bite you. After changing SITE_PROTECTION_PASSWORD or the enable flag, run php artisan config:cache (or config:clear) on the server. A stale cached config is the #1 reason "I changed the password and nothing happened."SESSION_DRIVER, SESSION_DOMAIN, or APP_KEY will invalidate existing visitors' access — expect everyone to be re-prompted after a deploy that touches those. If you're behind a proxy or load balancer, make sure TrustProxies is configured so the secure-cookie flag is set correctly.ERR_TOO_MANY_REDIRECTS on the password page. Fix in the except config, not by hacking the middleware.@csrf. A 419 on submit is nearly always this.throttle: middleware, and pick a long random password rather than something memorable.auth, verified, or custom middleware, the site gate should run early (global or first in the group) so unauthenticated visitors see the password form rather than a login redirect.X-Robots-Tag: noindex and a Disallow: / robots rule. The password form is a nice signal but isn't a crawler directive.php artisan octane:reload) — same class of bug as config:cache.How can I help you explore Laravel packages today?