laravel/framework · error · HttpException

Service Unavailable

Error message

Service Unavailable

What it means

Thrown as an HttpException (default status 503) by PreventRequestsDuringMaintenance when the app's maintenance mode is active, the request carries no valid bypass cookie/secret, and the maintenance data has no 'template' or 'redirect' key to render instead. It is the fallback 'down for maintenance' response.

Source

Thrown at src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php:101

            if (isset($data['redirect']) && ! $request->expectsJson()) {
                $path = $data['redirect'] === '/'
                    ? $data['redirect']
                    : trim($data['redirect'], '/');

                if ($request->path() !== $path) {
                    return redirect($path);
                }
            }

            if (isset($data['template']) && ! $request->expectsJson()) {
                return response(
                    $data['template'],
                    $data['status'] ?? 503,
                    $this->getHeaders($data)
                );
            }

            throw new HttpException(
                $data['status'] ?? 503,
                'Service Unavailable',
                null,
                $this->getHeaders($data)
            );
        }

        return $next($request);
    }

    /**
     * Determine if the incoming request has a maintenance mode bypass cookie.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  array  $data
     * @return bool
     */
    protected function hasValidBypassCookie($request, array $data)

View on GitHub (pinned to deac04fbdc)

Solutions

  1. Run php artisan up to disable maintenance mode.
  2. Provide a maintenance template via php artisan down --render='errors::503' (or a custom view) so a page renders instead of a bare exception.
  3. Add critical paths to PreventRequestsDuringMaintenance::except() so health checks/API still work.
  4. Use php artisan down --secret=... and visit the secret URL to obtain the bypass cookie for testing during downtime.

Example fix

// before
php artisan down  // bare maintenance, no template -> HttpException 503

// after
php artisan down --render='errors::maintenance' --retry=60
php artisan up  // when deploy complete
Defensive patterns

Strategy: try-catch

Try / catch

try {
    $response = $kernel->handle($request);
} catch (\Symfony\Component\HttpKernel\Exception\HttpException $e) {
    if ($e->getStatusCode() === 503) {
        return response()->json(['message' => 'Maintenance in progress.', 'retry' => $e->getHeaders()['Retry-After'] ?? null], 503);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Running php artisan down and then hitting any non-excluded route without the bypass cookie; the maintenance payload containing only status/retry without a template; a request that does not match the secret bypass path and lacks the laravel_maintenance cookie.

Common situations: Forgot to take the app out of maintenance (php artisan up) after a deploy; custom maintenance payload that omits 'template'; deploy scripts that run down but fail before up; CI hitting the app during a maintenance window.

Understand the failure class

Related errors


AI-assisted analysis of laravel/framework@deac04fbdc (2026-08-06). Data as JSON: /api/errors/b37adf6c8918d00b. Report an issue: GitHub.