configureRateLimiting(); $this->routes(function () { Route::prefix('api') ->middleware('api') ->namespace($this->namespace) ->group(base_path('routes/api.php')); Route::middleware('web') ->namespace($this->namespace) ->group(function () { require base_path('routes/web.php'); require base_path('routes/fortify.php'); // <-- Fortify routes, with some routes disabled as they are customized and therefore in web.php require base_path('routes/test.php'); }); }); // Route parameter constraints: Route::pattern('id', '[0-9]+'); // Explicit model binding for WireChat attachment (not in App\Models namespace) Route::model('attachment', WireChatAttachment::class); Broadcast::routes([ 'middleware' => ['auth.any:admin,bank,organization,web'], ]); } /** * Configure the rate limiters for the application. * * Public so tests can register the limiters again on a limiter rebuilt * over an in-memory cache (see PdfRateLimitTest). * * @return void */ public function configureRateLimiting() { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); }); // Public PDFs are rendered on every request and can take ~128 MB of // PHP memory each. On 2026-10-07 one crawler fetched them ~11 times a // second, the server ran out of memory and the kernel killed MariaDB. // A person downloading flyers never gets near 30 a minute; the // crawler ran at ~660. RateLimiter::for('pdf', function (Request $request) { return Limit::perMinute(30)->by($request->ip()); }); // Search runs an Elasticsearch query per request. Per user where // logged in (it is behind login), so people sharing an IP - a // community centre - do not share one allowance. RateLimiter::for('search', function (Request $request) { return Limit::perMinute(30)->by(optional($request->user())->id ?: $request->ip()); }); // Every Livewire interaction posts to livewire/update. On 2026-10-08 // one bot posted tampered payloads there for 9 hours, up to ~850 a // minute. Chat (WireChat) is the busiest real use: up to ~280 a minute // from one person with two browsers open. Logged-in people are counted // per user, so people sharing an IP do not share one allowance; guests // only see forms like login and registration and are counted per IP. RateLimiter::for('livewire', function (Request $request) { return $request->user() ? Limit::perMinute(500)->by('user:' . $request->user()->id) : Limit::perMinute(120)->by('ip:' . $request->ip()); }); } }