Anvil
Anvil - The mobile companion for Laravel Forge. Available now. Download for iOS

Usage

Quick Start

Enable Lockout

Add to your .env file:

1APP_READ_ONLY=true

By default, only pages accessed with GET requests will be allowed. All other request types (POST, PUT, PATCH, DELETE) will be blocked with a 401 Unauthorized response.

Disable Lockout

1APP_READ_ONLY=false

Or use the Artisan command:

1php artisan lockout:disable

Basic Configuration

Allow Login During Lockout

To allow users to log in and view secure areas (but still only allow GET requests after login):

1APP_READ_ONLY_LOGIN=true

You can customize your login/logout paths in the configuration file if they differ from Laravel defaults.

Locking Specific GET Pages

Block access to specific pages even for GET requests:

1'pages' => [
2 'register',
3 'subscribe',
4 'admin/settings',
5],

Whitelisting Specific Routes

Allow specific routes to bypass lockout:

1'whitelist' => [
2 'post' => 'password/confirm',
3 'put' => 'profile/update',
4],

Advanced Features

IP Whitelist/Blacklist

IP Whitelist

Allow specific IP addresses to bypass lockout completely:

Via .env:

1LOCKOUT_IP_WHITELIST=127.0.0.1,192.168.1.100

Via Config:

1'ip_whitelist_array' => [
2 '127.0.0.1',
3 '192.168.1.0/24', // CIDR notation supported
4 '10.0.0.0/8',
5],

IP Blacklist

Block specific IP addresses even when lockout is disabled:

Via .env:

1LOCKOUT_IP_BLACKLIST=192.168.1.50

Via Config:

1'ip_blacklist_array' => [
2 '192.168.1.50',
3 '10.0.0.100/24',
4],

Role-Based Exceptions

Allow users with specific roles to bypass lockout:

1'allowed_roles' => [
2 'admin',
3 'super-admin',
4 'maintenance',
5],

The package supports:

  • Laravel Bouncer
  • Spatie Permission
  • Simple role attributes ($user->role)
  • Roles relationships

Custom Response Types

View Response

Show a custom maintenance page:

1'response_type' => 'view',
2'response_view' => 'lockout::maintenance',
3'response_message' => 'We are currently performing maintenance.',
4'response_code' => 503,

Publish the view to customize:

1php artisan vendor:publish --tag=lockout-views

JSON Response

Return JSON for API requests:

1'response_type' => 'json',
2'response_code' => 503,

API-Specific Handling

Automatically detect and handle API requests differently:

1'api_enabled' => true,
2'api_response_type' => 'json',
3'api_response_message' => [
4 'message' => 'Application is in maintenance mode.',
5 'status' => 'maintenance',
6],

Route Patterns and Names

Route Patterns

Whitelist routes by pattern:

1'route_patterns' => [
2 'api/*', // All API routes
3 'health', // Health check
4 'admin/*', // All admin routes
5],

Route Names

Whitelist routes by name:

1'route_names' => [
2 'health.check',
3 'api.status',
4 'admin.dashboard',
5],

Health Check Endpoint

A health check endpoint is automatically registered at /health (configurable):

1GET /health

Response:

1{
2 "status": "ok",
3 "timestamp": "2025-01-15T10:30:00Z",
4 "lockout_enabled": true
5}

Disable or customize:

1'health_check_enabled' => true,
2'health_check_path' => 'health',

Cache Integration

Improve performance by caching lockout status:

1'cache_enabled' => true,
2'cache_key' => 'lockout.status',
3'cache_ttl' => 60, // seconds

Disable caching:

1'cache_enabled' => false,

Clear cache manually:

1php artisan cache:clear

Or when enabling/disabling:

1php artisan lockout:enable --clear-cache
2php artisan lockout:disable --clear-cache

Event System

The package fires events that you can listen to:

Available Events

  • Rappasoft\Lockout\Events\LockoutEnabled - Fired when lockout is enabled
  • Rappasoft\Lockout\Events\LockoutDisabled - Fired when lockout is disabled
  • Rappasoft\Lockout\Events\RequestBlocked - Fired when a request is blocked

Example Listener

1use Rappasoft\Lockout\Events\RequestBlocked;
2use Illuminate\Support\Facades\Log;
3 
4class LogBlockedRequests
5{
6 public function handle(RequestBlocked $event)
7 {
8 Log::warning('Request blocked', [
9 'path' => $event->request->path(),
10 'method' => $event->request->method(),
11 'ip' => $event->request->ip(),
12 'reason' => $event->reason,
13 ]);
14 }
15}

Register in EventServiceProvider:

1protected $listen = [
2 RequestBlocked::class => [
3 LogBlockedRequests::class,
4 ],
5];

Disable events:

1'fire_events' => false,

Artisan Commands

Enable Lockout

1php artisan lockout:enable

Options:

  • --clear-cache - Clear the lockout cache after enabling

Disable Lockout

1php artisan lockout:disable

Options:

  • --clear-cache - Clear the lockout cache after disabling

Check Status

1php artisan lockout:status

Output:

1Lockout Status:
2โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
3Enabled: Yes
4Cached: Yes
5
6Configuration:
7 Allow Login: Yes
8 Locked Types: post, put, patch, delete
9 Response Type: abort
10 Health Check: Yes
11 Cache Enabled: Yes

Blade Directive

Conditionally render content based on lockout status:

1@readonly
2 <div class="alert alert-warning">
3 Application is currently in read-only mode.
4 </div>
5@else
6 <div class="alert alert-info">
7 Application is fully operational.
8 </div>
9@endreadonly

Best Practices

  1. Use IP Whitelist for Maintenance: Whitelist your IP before enabling lockout to ensure you can still access the application.

  2. Use Health Check for Monitoring: Configure your monitoring tools to check the /health endpoint.

  3. Enable Caching in Production: Improve performance by enabling cache for lockout status.

  4. Use Events for Logging: Listen to RequestBlocked events to track blocked requests.

  5. Customize Response for Better UX: Use view responses for web requests and JSON for API requests.

  6. Test Before Production: Always test lockout in a staging environment first.