In Laravel 13, what do php artisan down's --render, --retry, --refresh and --redirect options do, and why does --render matter during a deploy?
answer
- prerendered HTML stored in the payload
- served before Composer's autoloader loads
- Retry-After and Refresh headers
- 302 to one path for browsers
- JSON requests fall through to Laravel
basics
~20 s--render stores a prerendered view that public/index.php can serve before the framework loads; --retry sets Retry-After; --refresh sets a Refresh header; --redirect sends browsers to one path. --render keeps the page working while vendor files are being replaced.
solid answer
~40 s`--render="errors::503"` renders the view once, when `down` runs, and stores the HTML in the maintenance payload. `public/index.php` requires `storage/framework/maintenance.php` before `vendor/autoload.php`, and that stub echoes the stored HTML and exits, so the page is served even while `composer install` is rewriting `vendor/`. Without `--render`, the framework must boot for the middleware to answer, and a half-updated `vendor/` can turn the 503 into an error. `--retry=60` (or a date) sets `Retry-After`; `--refresh=15` sets `Refresh`, so browsers reload by themselves; `--redirect=/` sends non-JSON requests to one path with a 302. Requests that expect JSON skip the prerendered page and get the framework's 503. The early exit only works with the file driver, because the stub reads `storage/framework/down`.
go deeper
Recall that --render sets the maintenance page, --refresh reloads browsers, --retry sets Retry-After and --redirect sends visitors to one path.
Explain how public/index.php serves the prerendered payload before the autoloader, and which requests fall through to the framework.
Use --render to survive dependency swaps during deploys, and know the cache driver removes that early-exit protection.
Decide whether the maintenance page must survive a broken framework boot, and choose the driver and deploy order to match.
## Why the options exist During a maintenance window on a streaming-subscription site, three groups hit the app: subscribers in browsers, the mobile app's API calls, and automated clients. The `down` command's options shape what each group sees. They are all stored in the maintenance **payload** that `down` writes (by default to `storage/framework/down`). ## The four options | Option | Payload key | Effect | |---|---|---| | `--render="errors::503"` | `template` | renders the view now and stores the HTML; served as the maintenance page | | `--retry=60` | `retry` | sends a `Retry-After` header; a number of seconds, or a date string converted to an HTTP date | | `--refresh=15` | `refresh` | sends a `Refresh` header, so browsers reload the page after that many seconds | | `--redirect=/` | `redirect` | sends browsers a 302 to that path for every other URL | A few details from the source: - `--retry` accepts a positive integer or any string Carbon can parse, which is stored in RFC 7231 format; anything else is dropped. - `--redirect` only applies to requests that do not expect JSON, and the target path itself is served the maintenance response, so there is no loop. - The docs note that browsers generally ignore `Retry-After`; it is mainly for crawlers and HTTP clients. ## Why `--render` matters during a deploy Without `--render`, a request in maintenance mode travels a long way before it is refused: Composer's autoloader loads, the application boots, service providers register, and only then does the maintenance middleware throw a 503 that the exception handler renders with the `503` error view. If the deploy is in the middle of `composer install`, some of those classes may be missing or mismatched, and the visitor gets a fatal error instead of a maintenance page. `--render` moves the work to the moment `down` runs. The command renders the view (with a `retryAfter` variable available) and stores the HTML in the payload. Then the request path becomes: 1. `public/index.php` checks for `storage/framework/maintenance.php` **before** requiring `vendor/autoload.php`; 2. the stub reads `storage/framework/down` and decodes the payload; 3. if the URI is excluded, matches the secret, or carries a valid bypass cookie, it returns and lets Laravel boot; 4. if the request expects JSON, it also returns, so the framework produces a proper 503 JSON response; 5. if `redirect` is set, it sends a 302; 6. otherwise it sets the status (503 by default), `Retry-After` and `Refresh` headers, echoes the stored HTML and exits. No Laravel class and no Composer file is touched on the common path, so the page is stable while `vendor/` changes underneath it. ## The driver caveat The stub only looks at the **file** at `storage/framework/down`. With `APP_MAINTENANCE_DRIVER=cache`, that file does not exist, so the stub returns immediately and the request boots the framework; the middleware then serves the stored template. The page still appears, but the protection against a half-updated `vendor/` directory is gone. Teams that switch to the cache driver for multi-server maintenance should know they traded that away. ## Choosing the template `--render` takes any view name. `errors::503` resolves through the `errors` view namespace, which `RegisterErrorViewPaths` points at `resources/views/errors` first and the framework's built-in error views second, so defining `resources/views/errors/503.blade.php` customises it. The view is rendered in the **console process** that runs `down`, not in a web request. That has consequences: - there is no current user, session or request, so the template must not greet the subscriber by name or read request data; - URLs generated with helpers such as `url()` or `asset()` are built from the configured application URL; - the view receives a `retryAfter` variable holding the `--retry` value, which it can print as an expected return time. ## Putting it together ```bash php artisan down --render="errors::503" --retry=600 --refresh=30 --with-secret ``` - subscribers see the site's own 503 page, which reloads every 30 seconds; - clients that read `Retry-After` wait ten minutes; - the mobile app, which sends `Accept: application/json`, gets a JSON 503 from the framework; - engineers bypass with the printed secret URL. The template is fixed at the moment `down` runs. Changing `resources/views/errors/503.blade.php` afterwards does nothing until `down` is run again.
- Why does php artisan down --render stop protecting the page when APP_MAINTENANCE_DRIVER=cache?The early-exit stub in `storage/framework/maintenance.php` only checks the file `storage/framework/down`. The cache driver stores the payload in a cache store instead, so the file is absent, the stub returns, and every request boots the framework before the middleware serves the template. A half-updated `vendor/` can then break the page again.
- The team edits resources/views/errors/503.blade.php during a Laravel maintenance window started with --render; why does the page not change?`--render` renders the view once, when `down` runs, and stores the resulting HTML in the payload. The stub and the middleware serve that stored string, not the Blade file. Running `php artisan down --render=...` again replaces the payload with a fresh rendering.
saying these in an interview costs you the question
- --render renders the Blade view on every maintenance request
- --retry makes browsers reload the page automatically
- --redirect also redirects JSON API requests
- The prerendered page works the same with the cache driver
- Without --render Laravel serves a blank 503 page