rasuvaeff / yii3-maintenance-mode
Maintenance mode middleware for Yii3 applications
Package info
github.com/rasuvaeff/yii3-maintenance-mode
pkg:composer/rasuvaeff/yii3-maintenance-mode
Requires
- php: ^8.3
- psr/http-factory: ^1.0
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.51
- friendsofphp/php-cs-fixer: ^3.95
- infection/infection: ^0.29
- maglnet/composer-require-checker: ^4.17
- phpunit/phpunit: ^11.5
- psalm/plugin-phpunit: ^0.19
- rector/rector: ^2.4
- vimeo/psalm: ^6.16
This package is auto-updated.
Last update: 2026-06-02 16:50:30 UTC
README
Maintenance mode PSR-15 middleware for Yii3. Returns HTTP 503 with Retry-After header. Supports IP allow-list, bypass token, JSON and HTML responses.
Using an AI coding assistant? llms.txt has a compact API reference ready to paste into context.
Requirements
- PHP 8.3+
psr/http-message^2.0psr/http-server-middleware^1.0
Installation
composer require rasuvaeff/yii3-maintenance-mode
Usage
1. Add middleware to the pipeline
MaintenanceMiddleware must be placed as early as possible — before routing and authentication. Otherwise requests reach the router even during maintenance.
// config/web.php or wherever your middleware stack is defined use Rasuvaeff\Yii3MaintenanceMode\MaintenanceMiddleware; return [ MaintenanceMiddleware::class, // ← first ErrorCatcher::class, Router::class, // ... ];
2. Configure in params.php
// config/params.php return [ 'rasuvaeff/yii3-maintenance-mode' => [ 'enabled' => false, 'retryAfter' => 300, 'allowedIps' => [], 'bypassTokenHash' => '', ], ];
The package ships config/di.php and config/params.php via config-plugin. ConfigMaintenanceProvider and MaintenanceMiddleware are registered automatically.
3. Enable maintenance mode
Via environment variable (no deploy required):
// config/params.php 'rasuvaeff/yii3-maintenance-mode' => [ 'enabled' => (bool) ($_ENV['MAINTENANCE_ENABLED'] ?? false), 'retryAfter' => 600, 'allowedIps' => ['10.0.0.1'], 'bypassTokenHash' => $_ENV['MAINTENANCE_BYPASS_HASH'] ?? '', ],
Via JSON file (toggle without deploy or restart):
// config/di.php — switch to FileMaintenanceProvider use Rasuvaeff\Yii3MaintenanceMode\FileMaintenanceProvider; use Rasuvaeff\Yii3MaintenanceMode\MaintenanceProvider; return [ MaintenanceProvider::class => [ 'class' => FileMaintenanceProvider::class, '__construct()' => [ 'filePath' => dirname(__DIR__) . '/maintenance.json', ], ], ];
Enable: echo '{"enabled":true,"retryAfter":600}' > maintenance.json
Disable: rm maintenance.json
Bypass token
Generate a hash of your secret token:
php -r "echo hash('sha256', 'my-secret-token');" # 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
Store the hash in config (never the token itself):
'bypassTokenHash' => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08',
Access any URL by appending ?bypass=my-secret-token:
https://example.com/?bypass=my-secret-token
https://example.com/admin/dashboard?bypass=my-secret-token
Token is compared with hash_equals() — timing-safe, no brute-force risk.
API reference
MaintenanceState
readonly class MaintenanceState { public bool $enabled = false; public int $retryAfter = 300; // seconds /** @var list<string> */ public array $allowedIps = []; public string $bypassTokenHash = ''; // sha256 of bypass token }
MaintenanceProvider (interface)
interface MaintenanceProvider { public function getState(): MaintenanceState; }
Implement this to create custom providers (DB, Redis, feature flag, etc.).
ConfigMaintenanceProvider
$provider = new ConfigMaintenanceProvider([ 'enabled' => true, 'retryAfter' => 600, 'allowedIps' => ['127.0.0.1', '10.0.0.1'], 'bypassTokenHash' => hash('sha256', 'secret'), ]);
State is immutable — set once at construction. Best for config/env sources.
FileMaintenanceProvider
$provider = new FileMaintenanceProvider(filePath: '/var/app/maintenance.json');
Reads state on every getState() call — changes take effect without restart.
Returns disabled state (enabled: false) when file is missing or invalid JSON.
maintenance.json format:
{
"enabled": true,
"retryAfter": 600,
"allowedIps": ["10.0.0.1"],
"bypassTokenHash": "9f86d081..."
}
MaintenanceMiddleware
$middleware = new MaintenanceMiddleware( provider: $provider, responseFactory: $responseFactory, );
Decision logic (in order):
| Condition | Action |
|---|---|
enabled === false |
Pass through |
REMOTE_ADDR in allowedIps |
Pass through |
Valid ?bypass= token |
Pass through |
| Otherwise | Return 503 |
Response format
JSON (API clients)
Returned when Accept: application/json or Accept header is absent:
HTTP/1.1 503 Service Unavailable Content-Type: application/json Retry-After: 600
{
"error": "Service Unavailable",
"message": "The server is currently undergoing maintenance.",
"retryAfter": 600
}
HTML (browsers)
Returned when Accept: text/html or any other non-JSON accept:
HTTP/1.1 503 Service Unavailable Content-Type: text/html; charset=utf-8 Retry-After: 600
A minimal HTML maintenance page is returned. Override by implementing your own MaintenanceProvider wrapper or MiddlewareInterface decorator.
Security
- Bypass token compared with
hash_equals()— timing-safe - Only the hash is stored, never the plaintext token
- IP allow-list uses strict string comparison on
REMOTE_ADDR FileMaintenanceProvidercatchesJsonExceptiongracefully — invalid file → disabled state (safe default)
Examples
See examples/ for detailed Yii3 wiring: providers, pipeline placement, bypass token, console command, env-based toggling.
Development
make install make build make cs:fix make mutation
License
BSD-3-Clause. See LICENSE.md.