Middleware
Postbox verwendet sechzehn Custom Middleware neben den Laravel-Standard-Middleware. Die Registrierung erfolgt in bootstrap/app.php (globale Middleware) und routes/api.php (API-Middleware).
Uebersicht
| Middleware | Scope | Zweck |
|---|---|---|
HandleRedirects | Global (prepend) | URL-Redirects vor Routing abfangen |
SecurityHeaders | Global (Web + API) | Sicherheits-Header auf jeder Response |
TrackErrorPages | Global (append) | HTTP-Fehler (4xx/5xx) fuer Monitoring loggen |
CheckBlockedUser | Web (global) | Gesperrte User sofort ausloggen |
EnsureLegalConsent | Web (global) | Datenschutz + AGB Zustimmung erzwingen |
TrackPageView | Web (global) | Server-seitiges Matomo-Tracking |
VerifyHealthToken | Einzelroute | Health-Endpoint Authentifizierung |
EnsureCurrentWorkspace | Watcher-Routen | Workspace in Session pruefen |
EnsureWorkspaceFromRoute | Workspace-Show | Workspace aus URL laden und Session synchronisieren |
RejectRevokedTokens | API (Collector) | Revozierte und IP-gesperrte Sanctum-Tokens ablehnen |
TrackApiTokenUsage | API (Collector) | Stuendliche API-Token-Nutzung aggregieren |
IncreaseMemoryForLogViewer | Einzelroute | Memory-Limit fuer Log Viewer temporaer erhoehen |
ContentFreshness | Explorer-Routen | HTTP-Caching-Header (ETag, Last-Modified) fuer oeffentliche Seiten |
RedirectToLocalizedUrl | Global (prepend) | Legacy-URLs ohne Locale-Prefix auf lokalisierte Version umleiten |
SetLocaleFromUrl | Oeffentliche Routen | App-Locale aus URL-Prefix ({locale}) setzen |
SetLocaleFromUser | App-Routen (auth) | App-Locale aus User-DB-Praeferenz setzen |
HandleRedirects
Location: app/Http/Middleware/HandleRedirects.php
Before-Middleware, die URL-Redirects (Legacy-URLs, Marketing-Redirects, Error-Monitor-Redirects) vor dem eigentlichen Routing prueft. Laeuft als erste Middleware im Stack (prepended).
Logik
- Pfad gegen Ausschluss-Liste pruefen (
/api/,/livewire/,/_debugbar/,/up, statische Assets) RedirectManager::findRedirect()sucht exakten oder Regex-Match inurl_redirects- Falls Match: Query-Parameter weiterleiten, Hit-Counter inkrementieren, Redirect mit konfiguriertem Status-Code
- Falls kein Match: Request normal weiterleiten
Fehlerverhalten
| Situation | Response |
|---|---|
| Exakter Redirect-Match | 301/302 Redirect zum Ziel-URL |
| Regex-Match | 301/302 Redirect mit Capture-Group-Replacement |
| Kein Match | Request wird weitergeleitet |
| Hit-Tracking-Fehler | Wird via report() geloggt, Redirect laeuft trotzdem |
Ausgeschlossene Pfade
/api/, /livewire/, /_debugbar/, /_ignition/, /up, /up_system, statische Assets (.css, .js, .png, .jpg, etc.)
Abhaengigkeiten
App\Services\ErrorMonitor\RedirectManager— Redirect-Lookup und Regex-AufloesungApp\Models\UrlRedirect— Redirect-Konfigurationen
SecurityHeaders
Location: app/Http/Middleware/SecurityHeaders.php
Setzt sicherheitsrelevante HTTP-Header auf jeder Response. Wird global fuer Web- und API-Requests angehaengt.
Gesetzte Header
| Header | Wert | Zweck |
|---|---|---|
X-Content-Type-Options | nosniff | Verhindert MIME-Type-Sniffing (XSS-Schutz) |
X-Frame-Options | DENY / SAMEORIGIN | Clickjacking-Schutz. SAMEORIGIN fuer Pulse, Vantage, Log Viewer (iFrame-Einbettung im Admin-Layout). |
Referrer-Policy | strict-origin-when-cross-origin | Begrenzt Referrer-Informationen an Dritte |
Permissions-Policy | camera=(), microphone=(), geolocation=(), fullscreen=(), payment=() | Deaktiviert nicht benoetigte Browser-APIs |
Strict-Transport-Security | max-age=31536000; includeSubDomains | HSTS (nur bei HTTPS-Requests, um lokale HTTP-Umgebungen nicht zu sperren) |
Content-Security-Policy | Dynamisch (siehe unten) | Schutz gegen XSS, Clickjacking, Data-Injection |
Content-Security-Policy (CSP)
Die CSP wird dynamisch aus der Config generiert — keine hardcodierten Domains. Alle externen Origins werden zur Laufzeit aus .env/Config-Werten abgeleitet.
| Direktive | Wert | Herkunft |
|---|---|---|
default-src | 'self' | Statisch |
script-src | 'self' 'unsafe-inline' 'unsafe-eval' + Turnstile + jsdelivr CDN + Matomo | config('services.matomo.url') |
style-src | 'self' 'unsafe-inline' + Bunny Fonts | Statisch |
img-src | 'self' data: blob: https: + CDN | config('filesystems.disks.public.url') |
font-src | 'self' data: + Bunny Fonts | Statisch |
connect-src | 'self' + Matomo + WebSocket + CDN | config('reverb.apps.apps.0.options.host/port'), s.o. |
worker-src | 'self' blob: | Statisch |
media-src | 'self' blob: | Statisch |
frame-src | 'self' + Turnstile + YouTube | Statisch |
object-src | 'none' | Statisch |
base-uri | 'self' | Statisch |
form-action | 'self' | Statisch |
Dynamische Origins:
- Matomo: URL aus
config('services.matomo.url')(z.B.https://ana.lyse.io) - WebSocket: Host + Port aus
config('reverb.apps.apps.0.options')(z.B.wss://app.postbox.so:443) - CDN: URL aus
config('filesystems.disks.public.url')(z.B.https://cdn.postbox.so) — nur wenn CDN-Host != App-Host
Hinweise:
'unsafe-eval'ist fuer Alpine.js erforderlich (nutztnew Function()fuer Expression-Evaluation)'unsafe-inline'ist fuer Livewire/Flux UI Inline-Styles erforderlichhttps://cdn.jsdelivr.netfuer ApexCharts (Score-Charts, Google API Usage, Publishing Analytics etc.)- Same-Origin iFrames (Pulse, Vantage, Log Viewer) werden ueber
'self'abgedeckt
Registrierung
// bootstrap/app.php
$middleware->append(SecurityHeaders::class);
$middleware->web(append: [
SecurityHeaders::class,
// ...
]);
$middleware->api(append: [
SecurityHeaders::class,
]);
Fehlerverhalten
Kein Fehlerverhalten -- die Middleware fuegt Header hinzu, blockiert aber keine Requests.
CheckBlockedUser
Location: app/Http/Middleware/CheckBlockedUser.php
Prueft bei jedem authentifizierten Web-Request, ob der User gesperrt ist (blocked_at IS NOT NULL). Gesperrte User werden sofort ausgeloggt und auf die Login-Seite weitergeleitet.
Logik
- Request hat keinen authentifizierten User? → Weiterleiten (kein Check noetig)
$user->isBlocked()prueftblocked_at IS NOT NULL- Gesperrt: Session invalidieren, ausloggen, Redirect zu
/login - Nicht gesperrt: Request weiterleiten
Fehlerverhalten
| Situation | Response |
|---|---|
| User gesperrt | Session invalidiert, 302 Redirect zu /login |
| User nicht gesperrt | Request wird weitergeleitet |
| Kein User authentifiziert | Request wird weitergeleitet |
Integration mit Fortify
Zusaetzlich zur Middleware wird der Login-Versuch gesperrter User direkt in FortifyServiceProvider abgefangen:
// app/Providers/FortifyServiceProvider.php
Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)->first();
if ($user?->isBlocked()) {
throw ValidationException::withMessages([
'email' => ['Diese Zugangsdaten sind ungültig.'],
]);
}
// ... normale Authentifizierung
});
Die generische Fehlermeldung verraet nicht, dass der Account gesperrt ist.
Registrierung
// bootstrap/app.php
$middleware->web(append: [
CheckBlockedUser::class,
// ...
]);
VerifyHealthToken
Location: app/Http/Middleware/VerifyHealthToken.php
Schuetzt den erweiterten Health-Endpoint (/up_system) vor unbefugtem Zugriff. Das Token wird gegen config('postbox.health.token') (Env: HEALTH_TOKEN) geprueft.
Akzeptierte Token-Quellen
| Quelle | Format | Typischer Nutzer |
|---|---|---|
| HTTP-Header | X-Health-Token: <token> | UptimeRobot, Monitoring-Tools |
| Query-Parameter | ?token=<token> | Browser-Zugriff |
Fehlerverhalten
| Situation | Response |
|---|---|
Kein Token konfiguriert (HEALTH_TOKEN leer) | 503 Service Unavailable (Plain Text) |
| Token fehlt oder falsch | 401 Unauthorized mit WWW-Authenticate: X-Health-Token Header |
Der Token-Vergleich nutzt hash_equals() zum Schutz gegen Timing-Angriffe.
Beispiel
# UptimeRobot-Style
curl -H "X-Health-Token: my-secret" https://app.postbox.so/up_system
# Browser-Style
curl "https://app.postbox.so/up_system?token=my-secret"
EnsureCurrentWorkspace
Location: app/Http/Middleware/EnsureCurrentWorkspace.php
Stellt sicher, dass ein Workspace in der Session gesetzt ist. Wird auf allen Watcher-bezogenen Routen verwendet (z.B. /watchers, /watcher/{watcher}).
Logik
- Liest den aktuellen Workspace aus
CurrentWorkspace::get()(Session-basiert). - Falls kein Workspace gesetzt ist: Redirect zu
/workspaces(Workspace-Auswahl). - Falls vorhanden: Request wird weitergeleitet.
Fehlerverhalten
| Situation | Response |
|---|---|
| Kein Workspace in Session | 302 Redirect zu workspaces.index |
Verwendung in Routen
// routes/web.php
Route::middleware(['auth', EnsureCurrentWorkspace::class])
->group(function () {
Route::get('/watchers', Watchers\Index::class);
Route::get('/watcher/{watcher}', Watchers\Show::class);
// ...
});
EnsureWorkspaceFromRoute
Location: app/Http/Middleware/EnsureWorkspaceFromRoute.php
Laedt den Workspace aus dem Route-Parameter ({workspace}) und synchronisiert ihn mit der Session. Unterstuetzt den Admin-Workspace (ID 999999999999).
Logik
- Liest
{workspace}aus dem Route-Parameter. - Admin-Check: Falls der User Admin ist und die Workspace-ID dem Admin-Workspace entspricht, wird der Admin-Workspace via
AdminWorkspaceManagersichergestellt. - Normal: Prueft, ob der User Mitglied des Workspace ist (
$user->workspaces()->whereKey(...)). - Setzt den Workspace in der Session via
CurrentWorkspace::set().
Fehlerverhalten
| Situation | Response |
|---|---|
| Kein User authentifiziert | 403 Forbidden |
| Workspace-ID fehlt | 404 Not Found |
| User ist kein Mitglied des Workspace (und kein Admin mit Admin-Workspace) | 403 Forbidden |
Verwendung in Routen
// routes/web.php
Route::get('/workspace/{workspace}', Watchers\Index::class)
->middleware(['auth', EnsureWorkspaceFromRoute::class])
->name('workspaces.show');
TrackPageView
Location: app/Http/Middleware/TrackPageView.php
Server-seitiges Matomo-Tracking fuer Page Views. Nutzt den MatomoTrackingService und dispatcht asynchrone Tracking-Requests.
Logik
Trackt nur wenn alle Bedingungen erfuellt sind:
| Bedingung | Grund |
|---|---|
| GET-Request | Nur Seitenaufrufe, keine Form-Submissions |
Kein X-Livewire-Header | Nur initiale Page Loads, keine Livewire-Updates |
| Status Code 200 | Keine Fehler, Redirects oder JSON-Responses |
| User ist kein Admin | Admin-Traffic verfaelscht Analytics |
Erfasste Daten
- URL, Seitentitel (aus
<title>-Tag oder Route-Name), User-ID (optional), IP-Adresse, User-Agent, Referrer, Accept-Language
Konfiguration
Die Middleware ist nur aktiv, wenn Matomo konfiguriert ist (MATOMO_ENABLED=true).
MATOMO_URL=https://analytics.example.com
MATOMO_SITE_ID=1
MATOMO_TOKEN=your-token
MATOMO_ENABLED=true
EnsureLegalConsent
Location: app/Http/Middleware/EnsureLegalConsent.php
Erzwingt Zustimmung zu Datenschutz und AGB fuer alle authentifizierten User. Bestandsuser (registriert vor Einfuehrung der Legal-Checkboxen) haben NULL in privacy_accepted_at/terms_accepted_at und werden auf die Consent-Seite umgeleitet.
Logik
- Kein authentifizierter User? → Weiterleiten
privacy_accepted_atUNDterms_accepted_atvorhanden? → Weiterleiten- Bereits auf Consent-Seite oder Logout? → Weiterleiten (Loop-Schutz)
- Sonst → Redirect zu
legal-consentRoute
Fehlerverhalten
| Situation | Response |
|---|---|
| User ohne Consent | 302 Redirect zu /legal-consent |
| User mit Consent | Request wird weitergeleitet |
| Unauthentifiziert | Request wird weitergeleitet |
Verbundene Components
app/Livewire/LegalConsent.php— Consent-Seite mit Checkboxen, CMS-Modal-Vorschau und Logout-Option
TrackErrorPages
Location: app/Http/Middleware/TrackErrorPages.php
After-Middleware, die HTTP-Fehler-Responses (Status 400–599) fuer das Error-Monitoring loggt. Laeuft nach der Response-Generierung (appended).
Logik
- Response-Status < 400 oder > 599? → Kein Tracking, sofort zurueckgeben
- Pfad gegen Ausschluss-Liste pruefen (
isExcludedPath()) - Statische Assets ueberspringen
ErrorMonitorService::recordErrorSimple()— PostgreSQL UPSERT (1 Zeile pro Pfad + Status-Code + Stunde)- Tracking-Fehler werden via
report()geloggt, die Response wird nie beeinflusst
Fehlerverhalten
| Situation | Response |
|---|---|
| Error-Status (4xx/5xx) | Response + asynchrones Logging |
| Success-Status (2xx/3xx) | Response ohne Tracking |
| Logging-Fehler | Wird geloggt, Response bleibt unberuehrt |
Erfasste Daten
- Pfad (lowercase), Status-Code, Referer, User-Agent
- Aggregation per UPSERT:
hit_countwird pro Stunde hochgezaehlt
Abhaengigkeiten
App\Services\ErrorMonitor\ErrorMonitorService
RejectRevokedTokens
Location: app/Http/Middleware/RejectRevokedTokens.php
Prueft API-Requests mit Sanctum-Token auf Soft-Revocation (revoked_at) und IP-Whitelist (allowed_ips). Laeuft nach auth:sanctum in der API-Middleware-Chain.
Logik
- Kein Token vorhanden? → Request weiterleiten (wird von
auth:sanctumbehandelt) $token->revoked_at !== null? →401 Unauthorized$token->isIpAllowed($request->ip())gibtfalsezurueck? →403 Forbidden- Sonst → Request weiterleiten
Fehlerverhalten
| Situation | Response |
|---|---|
| Token revoziert | 401 Unauthorized — "Token has been revoked." |
| IP nicht in Whitelist | 403 Forbidden — "Request IP not allowed for this token." |
| Keine Whitelist konfiguriert | Request wird weitergeleitet (alle IPs erlaubt) |
| Kein Token | Request wird weitergeleitet |
Registrierung
// routes/api.php
Route::middleware(['auth:sanctum', RejectRevokedTokens::class, 'throttle:collector', TrackApiTokenUsage::class])
->group(/* Collector-Routes */);
TrackApiTokenUsage
Location: app/Http/Middleware/TrackApiTokenUsage.php
After-Middleware, die API-Request-Counts pro Token und Stunde aggregiert. Nutzt PostgreSQL UPSERT fuer atomare Zaehler-Inkremente.
Logik
- Kein Sanctum-Token? → Kein Tracking
- Aktuelle Stunde als Bucket:
now()->startOfHour() - UPSERT in
api_token_usage_logs: Falls Eintrag existiert →request_count + 1, sonst neuer Eintrag mitrequest_count = 1
Daten-Modell
| Tabelle | Spalte | Typ | Beschreibung |
|---|---|---|---|
api_token_usage_logs | personal_access_token_id | FK | Token-Referenz |
hour | timestamp | Stunden-Bucket | |
request_count | uint | Akkumulierter Zaehler |
Fehlerverhalten
Tracking-Fehler werden via report() geloggt. Die API-Response wird nie beeinflusst.
Retention
Alte Usage-Logs werden via model:prune bereinigt. Retention: config('postbox.api_tokens.usage_retention_days', 90) Tage.
IncreaseMemoryForLogViewer
Location: app/Http/Middleware/IncreaseMemoryForLogViewer.php
Temporaere Memory-Limit-Erhoehung fuer den Log Viewer (/admin/logs). Der Log Viewer benoetigt mehr als die Standard-128MB, da grosse Log-Dateien geladen werden.
Logik
- Aktuelles
memory_limitmerken - Auf
256Merhoehen - Request an Log Viewer weiterleiten
- Sicheres Restaurieren: Memory-Limit nur zuruecksetzen, wenn der aktuelle Speicherverbrauch (
memory_get_usage(true)) unter dem vorherigen Limit liegt. Verhindert Crash wenn PHP waehrend des Requests bereits mehr als 128MB allokiert hat.
parseMemoryLimit() Helper
Parst PHP Memory-Strings (128M, 1G, -1) in Bytes fuer den Vergleich. Unterstuetzt K/M/G-Suffixe und den Spezialwert -1 (unlimited).
Fehlerverhalten
| Situation | Response |
|---|---|
| Speicherverbrauch < vorheriges Limit | Limit wird restauriert |
| Speicherverbrauch >= vorheriges Limit | Limit bleibt auf 256M (kein Crash) |
Vorheriges Limit ist -1 (unlimited) | Limit wird immer restauriert |
Registrierung
Die Middleware wird direkt an der Log-Viewer-Route angehaengt:
// routes/web.php
Route::middleware(['auth', IncreaseMemoryForLogViewer::class])
->get('/admin/logs', ...);
ContentFreshness
Location: app/Http/Middleware/ContentFreshness.php
Fuegt HTTP-Caching-Header (Last-Modified, ETag) zu oeffentlichen Seiten-Responses hinzu fuer effizientes Client- und CDN-Caching. Reduziert Bandbreite, indem AI-Crawler, Suchmaschinen und Browser unveraenderten Content nicht erneut herunterladen muessen.
Logik
- Response generieren (next Middleware)
- Nur bei Status 200 fortfahren — alle anderen Status uebergehen
- ETag aus MD5 des Response-Contents generieren
Last-ModifiedHeader auf aktuelle GMT-Zeit setzen (falls nicht vorhanden)- Client-
If-None-MatchHeader pruefen: Falls ETag uebereinstimmt → 304 Not Modified zurueckgeben - Sonst:
Cache-Control: public, max-age=300, must-revalidatesetzen
Fehlerverhalten
| Situation | Response |
|---|---|
| Status ≠ 200 | Skip, Response unveraendert |
| Kein Response-Content | Skip, Response unveraendert |
| ETag Match | 304 Not Modified |
| Kein ETag Match | Response mit Caching-Headern |
Registrierung
Route-Middleware — nur auf /explorer/* oeffentliche Routen angewandt.
RedirectToLocalizedUrl
Location: app/Http/Middleware/RedirectToLocalizedUrl.php
Faengt Legacy-URLs ohne Locale-Prefix (z.B. /login, /explorer) ab und leitet sie per 301-Redirect auf lokalisierte Versionen um (z.B. /de/login, /en/explorer). Erkennt die bevorzugte Locale aus: Query-Parameter (?locale=), User-DB-Praeferenz, Cookie oder Default (de).
Logik
- Nur GET-Requests verarbeiten (POST/PUT/DELETE durchlassen fuer Fortify-Actions)
- Pruefen ob Pfad oeffentlich ist UND kein Locale-Prefix hat (
/de/oder/en/) - Falls ja: Bevorzugte Locale ermitteln (Prioritaet:
?locale=→ User-DB → Cookie →de) ?locale=aus Query-String entfernen (wird konsumiert)- Redirect-Pfad bauen:
/{locale}/{path}mit verbleibenden Query-Parametern - 301-Redirect mit
locale-Cookie (30 Tage) zurueckgeben
Fehlerverhalten
| Situation | Response |
|---|---|
| GET-Request, oeffentlich, kein Locale-Prefix | 301 Redirect zu /{locale}/{path} + Cookie |
| POST/PUT/DELETE | Request wird weitergeleitet |
| Pfad hat bereits Locale-Prefix | Request wird weitergeleitet |
Ungueltige ?locale= | Naechste Prioritaet verwenden |
Registrierung
// bootstrap/app.php
$middleware->prepend(RedirectToLocalizedUrl::class);
Laeuft als zweite globale Middleware nach HandleRedirects.
SetLocaleFromUrl
Location: app/Http/Middleware/SetLocaleFromUrl.php
Setzt die App-Locale aus dem URL-Prefix ({locale}) auf oeffentlichen Seiten. Liest den Route-Parameter und konfiguriert Laravel entsprechend. Persistiert die Gast-Locale-Wahl per Cookie (30 Tage).
Logik
localeRoute-Parameter lesen (Default:de)- Gegen
User::SUPPORTED_LOCALESvalidieren; ungueltig →de app()->setLocale($locale)setzenDate::setLocale($locale)fuer locale-aware DatumsformatierungURL::defaults(['locale' => $locale])damitroute()-Calls den{locale}-Parameter automatisch fuellen- Response mit
locale-Cookie (30 Tage TTL) zurueckgeben
Fehlerverhalten
| Situation | Response |
|---|---|
| Gueltiges Locale (de/en) | Locale gesetzt, Cookie gesetzt |
| Ungueltiges oder fehlendes Locale | Fallback auf de |
Registrierung
// bootstrap/app.php — Route-Alias
'set-locale-from-url' => SetLocaleFromUrl::class,
Angewandt auf oeffentliche Route-Gruppen mit {locale}-Parameter.
SetLocaleFromUser
Location: app/Http/Middleware/SetLocaleFromUser.php
Setzt die App-Locale aus der DB-Praeferenz des authentifizierten Users (users.locale-Spalte) fuer den App-Bereich (Dashboard, Settings, Admin). Die Locale wird NICHT aus der URL gelesen, sondern aus der gespeicherten User-Praeferenz.
Logik
- Default-Locale
deinitialisieren - Authentifizierten User pruefen: Falls vorhanden,
localegegenUser::SUPPORTED_LOCALESvalidieren app()->setLocale($locale)setzenDate::setLocale($locale)fuer locale-aware DatumsformatierungURL::defaults(['locale' => $locale])fuer locale-aware Route-Generierung- Kein Cookie gesetzt (User-DB-Praeferenz ist persistent)
Fehlerverhalten
| Situation | Response |
|---|---|
| User authentifiziert, Locale gueltig | User-Locale gesetzt |
| User authentifiziert, Locale ungueltig | Fallback auf de |
| Nicht authentifiziert | Default de |
Registrierung
// bootstrap/app.php — Route-Alias
'set-locale-from-user' => SetLocaleFromUser::class,
Angewandt auf geschuetzte App-Routen (Dashboard, Settings, Admin).
Middleware-Registrierung (bootstrap/app.php)
Die vollstaendige Middleware-Konfiguration in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware): void {
// Inline comment: HandleRedirects runs BEFORE all other middleware to redirect early.
$middleware->prepend(HandleRedirects::class);
// Inline comment: RedirectToLocalizedUrl catches legacy URLs without locale prefix.
$middleware->prepend(RedirectToLocalizedUrl::class);
// Apply security headers to every response, including health checks.
$middleware->append(SecurityHeaders::class);
// Inline comment: TrackErrorPages runs AFTER all other middleware to capture error responses.
$middleware->append(TrackErrorPages::class);
// Web-Middleware-Stack
$middleware->web(append: [
\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
SecurityHeaders::class,
CheckBlockedUser::class,
TrackPageView::class,
]);
// API-Middleware-Stack
$middleware->api(append: [
SecurityHeaders::class,
]);
// Route-Middleware-Aliases (Plan 33: i18n)
$middleware->alias([
'set-locale-from-url' => SetLocaleFromUrl::class,
'set-locale-from-user' => SetLocaleFromUser::class,
'redirect-to-localized' => RedirectToLocalizedUrl::class,
]);
})
Ausfuehrungsreihenfolge:
HandleRedirects(prepended) — Redirects vor RoutingRedirectToLocalizedUrl(prepended) — Legacy-URLs auf lokalisierte Variante umleiten- Routing, Controller, Livewire etc.
- Route-spezifische Aliases (
set-locale-from-url,set-locale-from-user) je nach Route-Gruppe SecurityHeaders,TrackErrorPages(appended) — Header + Error-Tracking
VerifyHealthToken, EnsureCurrentWorkspace, EnsureWorkspaceFromRoute und ContentFreshness werden nicht global registriert, sondern direkt an den jeweiligen Routen angehaengt.