Skip to content

Custom Error Pages Analyzer

Analyzer IDCategorySeverityTime To Fix
custom-error-pages✅ ReliabilityLow30 minutes

What This Checks

This analyzer validates the conventional Laravel approach of creating individual error template files in resources/views/errors/:

  • 401.blade.php - Unauthorized (authentication required)
  • 403.blade.php - Forbidden (authorization failed)
  • 404.blade.php - Not Found
  • 419.blade.php - Page Expired (CSRF token mismatch)
  • 429.blade.php - Too Many Requests (rate limiting)
  • 500.blade.php - Internal Server Error
  • 503.blade.php - Service Unavailable (maintenance mode)

What this analyzer checks:

  • Existence of conventional error template files on the filesystem
  • All configured view paths and custom error view namespaces
  • Skips stateless/API-only apps (no session middleware)

What this analyzer does NOT check:

  • Custom exception handlers in app/Exceptions/Handler.php
  • Dynamic error views (single template handling multiple HTTP codes)
  • Middleware-level error response overrides
  • Custom render methods on exception classes
  • Error responses returned from controllers

Important

Applications can implement custom error handling through various approaches. If you use custom exception handlers or other non-conventional methods, you can safely ignore this warning.

Why It Matters

  • Framework fingerprinting: Default error pages advertise "Laravel", helping attackers tailor exploits
  • Security exposure: CSRF errors (419) and authorization failures (401, 403) revealing framework details can guide attacks
  • User experience: Users prefer branded error pages with recovery instructions (contact support, try again)
  • Rate limiting clarity: Custom 429 pages can explain retry timing and help prevent support tickets
  • Consistency: Custom error pages maintain brand experience even during failures or maintenance

How to Fix

Custom error handling can be implemented in several ways. Choose the approach that best fits your architecture:

Approach 1: Conventional File-Based (Checked by This Analyzer)

Quick Fix (15 minutes):

  1. Publish the default error views:
bash
php artisan vendor:publish --tag=laravel-errors
  1. Customize all error templates in resources/views/errors/:
    • 401.blade.php - Unauthorized
    • 403.blade.php - Forbidden
    • 404.blade.php - Not Found
    • 419.blade.php - Page Expired (CSRF)
    • 429.blade.php - Too Many Requests
    • 500.blade.php - Internal Server Error
    • 503.blade.php - Service Unavailable

Best Practices:

  • Add helpful CTAs (support links, status pages, recovery instructions)
  • Display correlation/request IDs for debugging
  • Localize error messages for multilingual apps
  • Write feature tests to verify templates render correctly

Approach 2: Custom Exception Handler

Override the render() method in app/Exceptions/Handler.php:

php
public function render($request, Throwable $e)
{
    if ($e instanceof NotFoundHttpException) {
        return response()->view('errors.custom-404', [], 404);
    }

    if ($e instanceof AuthorizationException) {
        return response()->view('errors.custom-403', [], 403);
    }

    return parent::render($request, $e);
}

Approach 3: Dynamic Error View

Create a single template that handles all error codes:

blade
{{-- resources/views/errors/error.blade.php --}}
@switch($exception->getStatusCode())
    @case(404)
        <h1>Page Not Found</h1>
    @case(500)
        <h1>Server Error</h1>
    @default
        <h1>An Error Occurred</h1>
@endswitch

Approach 4: Middleware-Based

Transform error responses in middleware:

php
public function handle($request, Closure $next)
{
    $response = $next($request);

    if ($response->status() >= 400) {
        return response()->view('errors.handler', [
            'code' => $response->status()
        ], $response->status());
    }

    return $response;
}

Skipping This Analyzer

If you use approaches 2-4, add to config/shieldci.php:

php
'dont_report' => [
    'custom-error-pages',
],

ShieldCI Configuration

This analyzer is automatically skipped for stateless/API-only applications.

Why skip for stateless/API-only apps?

  • API-only applications return JSON error responses, not HTML pages
  • Stateless apps (no session middleware) don't serve traditional web pages
  • Custom HTML error pages aren't relevant for pure API applications

When to run this analyzer:

  • Web applications with sessions: Full-stack Laravel apps serving HTML
  • Hybrid apps: Applications with both web and API routes
  • Local development: Ensures error pages are configured before deployment
  • Staging/Production servers: Validates custom error pages are in place
  • API-only applications: Skipped automatically (no session middleware detected)

References