Zum Hauptinhalt springen

Überblick

Wenn Sie ein neues Laravel-Projekt anlegen, sind Fehler- und Ausnahmebehandlung bereits vorkonfiguriert. Anpassungen nehmen Sie über die Methode withExceptions in bootstrap/app.php vor.

Ablauf der Ausnahmebehandlung

Vom Auslösen einer Ausnahme bis zur Antwort an den Client durchläuft Laravel folgende Schritte:
// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;

return Application::configure(basePath: dirname(__DIR__))
    ->withExceptions(function (Exceptions $exceptions): void {
        // Reporting und Rendering von Ausnahmen hier konfigurieren
    })->create();
Das an die withExceptions-Closure übergebene Objekt $exceptions ist eine Instanz von Illuminate\Foundation\Configuration\Exceptions und verwaltet die gesamte Ausnahmebehandlung Ihrer Anwendung.

Debug-Einstellungen

Die Option debug in config/app.php steuert, wie viele Fehlerinformationen ausgegeben werden. Standardmäßig übernimmt sie den Wert der Umgebungsvariable APP_DEBUG aus der .env-Datei.
# Lokale Entwicklung
APP_DEBUG=true

# Produktion
APP_DEBUG=false
Setzen Sie APP_DEBUG in der Produktion unbedingt auf false. Andernfalls können sensible Informationen an Endnutzer gelangen.

Ausnahmen melden

Beim Reporting wird eine Ausnahme protokolliert oder an externe Dienste wie Sentry oder Flare gesendet. Standardmäßig wird gemäß den Einstellungen in config/logging.php in ein Log geschrieben.

Eigene Report-Callbacks

Möchten Sie je nach Ausnahmetyp unterschiedliche Report-Logik ausführen, übergeben Sie eine Closure an die Methode report. Laravel erkennt den Ausnahmetyp anhand der Type-Hints der Closure.
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // Benachrichtigung an externen Dienst usw.
    });
})
Auch wenn Sie eine eigene Callback registrieren, wird das Standard-Logging weiter ausgeführt. Um die Standard-Propagation abzubrechen, rufen Sie stop() auf oder geben Sie false zurück.
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // ...
    })->stop();
})

Helper report()

Wenn Sie eine Ausnahme ausschließlich melden möchten, ohne eine Fehlerseite anzuzeigen, verwenden Sie den Helper report().
public function isValid(string $value): bool
{
    try {
        // Validierung ...
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}
Mit dem Helper report() protokollieren Sie Fehler, ohne die Nutzerantwort zu unterbrechen. Ideal für nebensächliche Ausnahmen in Hintergrund-Jobs.

Doppelte Reports verhindern

Wird dieselbe Ausnahme-Instanz mehrfach an report() übergeben, entstehen doppelte Log-Einträge. Mit dontReportDuplicates() protokolliert Laravel dieselbe Instanz nur beim ersten Mal.
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportDuplicates();
})
$original = new RuntimeException('Whoops!');

report($original); // Wird protokolliert

try {
    throw $original;
} catch (Throwable $caught) {
    report($caught); // Wird ignoriert (dieselbe Instanz)
}

Globaler Log-Kontext

Um allen Log-Einträgen zu Ausnahmen dieselben Informationen anzuhängen, verwenden Sie context. Die aktuell angemeldete Benutzer-ID wird, sofern verfügbar, automatisch mitgegeben.
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->context(fn () => [
        'app_version' => config('app.version'),
    ]);
})

context()-Methode in der Ausnahmeklasse

Definieren Sie in einer eigenen Ausnahmeklasse die Methode context(), um spezifische Kontextinformationen an das Log zu übergeben.
<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    public function __construct(
        private readonly int $orderId,
        string $message = '',
    ) {
        parent::__construct($message);
    }

    /**
     * Kontextinformationen der Ausnahme zurückgeben.
     *
     * @return array<string, mixed>
     */
    public function context(): array
    {
        return ['order_id' => $this->orderId];
    }
}

Log-Level anpassen

Um eine bestimmte Ausnahme mit einem bestimmten Log-Level zu erfassen, verwenden Sie die Methode level.
use PDOException;
use Psr\Log\LogLevel;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->level(PDOException::class, LogLevel::CRITICAL);
})

Reports drosseln

Wenn massenhaft Ausnahmen auftreten, können Sie die Reports mit throttle steuern.
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    // Nur zufällig 1 von 1000 Fällen melden
    $exceptions->throttle(function (Throwable $e) {
        return Lottery::odds(1, 1000);
    });
})
Für eine Begrenzung pro Minute verwenden Sie Limit.
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof BroadcastException) {
            return Limit::perMinute(300);
        }
    });
})

Ausnahmen rendern

Beim Rendering wird eine Ausnahme in eine HTTP-Antwort umgewandelt. Standardmäßig erzeugt Laravel eine passende Antwort automatisch; Anpassungen sind aber möglich.

Eigene Render-Callbacks

Übergeben Sie an die Methode render eine Closure, um die Ausnahme in eine Antwort umzuwandeln.
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });
})
Auch das Rendering integrierter Ausnahmen (etwa NotFoundHttpException) lässt sich überschreiben. Liefert die Closure keinen Wert, wird das Standardrendering verwendet.
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.',
            ], 404);
        }
        // null zurückgeben → Standard-404-Seite anzeigen
    });
})

Automatische Erkennung von JSON/HTML

Laravel entscheidet anhand des Accept-Headers, ob HTML oder JSON zurückgegeben wird. Um diese Logik anzupassen, nutzen Sie shouldRenderJsonWhen.
use Illuminate\Http\Request;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        if ($request->is('admin/*')) {
            return true; // Adminbereich immer als JSON
        }

        return $request->expectsJson();
    });
})

Antwort insgesamt anpassen

Mit respond können Sie die erzeugte Antwort weiter verändern.
use Symfony\Component\HttpFoundation\Response;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->respond(function (Response $response) {
        if ($response->getStatusCode() === 419) {
            return back()->with([
                'message' => 'Die Seite ist abgelaufen. Bitte versuchen Sie es erneut.',
            ]);
        }

        return $response;
    });
})

Eigene Ausnahmeklassen

Sie können in app/Exceptions/ eigene Ausnahmeklassen anlegen. Definieren Sie in der Klasse Methoden report() und render(), werden sie automatisch aufgerufen – auch ohne Konfiguration in bootstrap/app.php.

Ausnahmeklasse erzeugen

1

Ausnahmeklasse erstellen

php artisan make:exception InvalidOrderException
2

report() und render() implementieren

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class InvalidOrderException extends Exception
{
    public function __construct(
        private readonly int $orderId,
        string $message = 'Invalid order.',
    ) {
        parent::__construct($message);
    }

    /**
     * Ausnahme melden.
     */
    public function report(): void
    {
        // Benachrichtigung an externen Dienst usw.
    }

    /**
     * Ausnahme in eine HTTP-Antwort umwandeln.
     */
    public function render(Request $request): Response
    {
        return response()->view('errors.invalid-order', [
            'orderId' => $this->orderId,
        ], 422);
    }
}
Die report()-Methode unterstützt Dependency Injection per Type-Hint. Der Service Container von Laravel löst die Abhängigkeiten automatisch auf.

Interface ShouldntReport

Für Ausnahmen, die nicht gemeldet werden sollen, implementieren Sie das Interface ShouldntReport. Solche Ausnahmen werden nie gemeldet.
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;

class PodcastProcessingException extends Exception implements ShouldntReport
{
    //
}

Ausnahmen auslösen

Helper abort()

Sie können von überall in der Anwendung eine HTTP-Fehlerantwort auslösen.
// 404 Not Found
abort(404);

// Mit Nachricht
abort(403, 'Sie sind nicht berechtigt, diese Aktion auszuführen.');

abort_if() / abort_unless()

Helfer, um Ausnahmen bedingt auszulösen.
// Wenn $condition true ist, abbrechen
abort_if(! $user->isAdmin(), 403);

// Wenn $condition false ist, abbrechen
abort_unless($user->hasPermission('edit'), 403, 'Permission denied.');
Praktisch bei Berechtigungsprüfungen in Controllern oder Middleware, häufig in Kombination mit Gates und Policies.

Globale Steuerung von Ausnahmen

Ausnahmen gezielt ignorieren

Über dontReport bestimmen Sie Ausnahmen, die nicht gemeldet werden sollen. Individuelles Rendering bleibt weiter aktiv.
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReport([
        InvalidOrderException::class,
    ]);
})
Für bedingtes Ignorieren übergeben Sie eine Closure an dontReportWhen.
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportWhen(function (Throwable $e) {
        return $e instanceof PodcastProcessingException &&
               $e->reason() === 'Subscription expired';
    });
})
Standardmäßig ignoriert Laravel bereits einige Ausnahmen wie 404-Fehler, CSRF-Token-Verstöße (419) und Origin-Konflikte (403).

Standardmäßig ignorierte Ausnahmen wieder melden

Um Ausnahmen, die Laravel standardmäßig ignoriert, wieder zu melden, verwenden Sie stopIgnoring.
use Symfony\Component\HttpKernel\Exception\HttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->stopIgnoring(HttpException::class);
})

HTTP-Fehlerseiten

Zu jedem HTTP-Statuscode können Sie eigene Fehlerseiten definieren.

Eigene Fehler-Views erstellen

Legen Sie im Verzeichnis resources/views/errors/ Blade-Vorlagen an, deren Dateiname dem Statuscode entspricht.
resources/
└── views/
    └── errors/
        ├── 404.blade.php
        ├── 403.blade.php
        └── 500.blade.php
Innerhalb der View steht die Variable $exception mit den Fehlerdetails zur Verfügung.
{{-- resources/views/errors/404.blade.php --}}
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Seite nicht gefunden</title>
</head>
<body>
    <h1>404 – Seite nicht gefunden</h1>
    <p>{{ $exception->getMessage() }}</p>
    <a href="{{ url('/') }}">Zur Startseite</a>
</body>
</html>

Standard-Fehlervorlagen veröffentlichen

Wenn Sie die Standard-Fehlerseiten von Laravel als Ausgangspunkt verwenden möchten, holen Sie sie mit vendor:publish.
php artisan vendor:publish --tag=laravel-errors

Fallback-Fehlerseiten

Als Fallback für Statuscodes ohne eigene View können Sie 4xx.blade.php und 5xx.blade.php anlegen.
resources/
└── views/
    └── errors/
        ├── 4xx.blade.php  # Fallback für 4xx
        └── 5xx.blade.php  # Fallback für 5xx
Für 404, 500 und 503 liefert Laravel bereits eigene Standardseiten. Wenn Sie diese anpassen möchten, legen Sie statt eines Fallbacks eigene Dateien (404.blade.php usw.) an.

Praxisbeispiel: API-Ausnahmehandler

Bei APIs möchten Sie Ausnahmen immer als JSON zurückgeben. Nachfolgend ein Beispiel, wie Sie API-Fehler zentral in bootstrap/app.php verwalten.
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    // API-Requests immer als JSON beantworten
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Ressource nicht gefunden.',
            ], 404);
        }
    });

    $exceptions->render(function (AuthenticationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Authentifizierung erforderlich.',
            ], 401);
        }
    });

    $exceptions->render(function (ValidationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Validierungsfehler.',
                'errors'  => $e->errors(),
            ], 422);
        }
    });
})

Eigene API-Ausnahmeklasse

Eine gemeinsame Basisklasse für API-Ausnahmen ermöglicht konsistente Fehlerantworten aus jedem Endpunkt.
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class ApiException extends Exception
{
    public function __construct(
        string $message = 'An error occurred.',
        private readonly int $statusCode = 500,
        private readonly array $errors = [],
    ) {
        parent::__construct($message);
    }

    public function render(Request $request): JsonResponse
    {
        $data = ['message' => $this->getMessage()];

        if (! empty($this->errors)) {
            $data['errors'] = $this->errors;
        }

        return response()->json($data, $this->statusCode);
    }
}
Verwendung im Controller:
<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\ApiException;
use App\Models\Order;

class OrderController extends Controller
{
    public function show(int $id): JsonResponse
    {
        $order = Order::find($id);

        if (! $order) {
            throw new ApiException('Bestellung nicht gefunden.', 404);
        }

        if ($order->isCancelled()) {
            throw new ApiException('Diese Bestellung wurde bereits storniert.', 422);
        }

        return response()->json($order);
    }
}

Zusammenfassung

VorgehenZweck
$exceptions->report()Eigene Report-Logik pro Ausnahmetyp registrieren
$exceptions->context()Allen Ausnahme-Logs gemeinsame Informationen anhängen
context()-MethodeAusnahmeklasse liefert eigenen Kontext
Helper report()Ausnahme melden, ohne die Antwort zu unterbrechen
dontReportDuplicates()Doppelte Reports derselben Instanz vermeiden
Interface ShouldntReportAusnahmeklassen erzeugen, die nie gemeldet werden
VorgehenZweck
$exceptions->render()Eigene Antwort pro Ausnahmetyp
render()-MethodeRendering-Logik direkt in der Ausnahmeklasse
shouldRenderJsonWhen()Erkennung JSON/HTML anpassen
respond()Erzeugte Antwort weiter verändern
  • Erstellen Sie Dateien wie resources/views/errors/404.blade.php – Laravel nutzt sie automatisch.
  • Über die Variable $exception erhalten Sie die Fehlerdetails.
  • Mit php artisan vendor:publish --tag=laravel-errors holen Sie sich die Standardvorlagen.
  • 4xx.blade.php / 5xx.blade.php dienen als Fallback-Seiten.
  • Setzen Sie APP_DEBUG=false und blenden Sie Stacktraces vor Nutzern aus.
  • Binden Sie externe Error-Tracker wie Sentry oder Flare an und verwalten Sie Fehler zentral.
  • Nutzen Sie throttle(), um Log-Fluten bei massenhaften Ausnahmen zu vermeiden.
  • Halten Sie bei API-Endpunkten ein konsistentes JSON-Fehlerformat ein.
Zuletzt geändert am 13. Juli 2026