diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2812886..afbd578 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -5,6 +5,7 @@ namespace App\Providers; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; use Illuminate\Support\Facades\RateLimiter; +use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -23,6 +24,16 @@ class AppServiceProvider extends ServiceProvider public function boot(): void { $this->configureRateLimiting(); + + // Belt-and-suspenders alongside bootstrap/app.php's trustProxies(): + // that already makes url()/asset() respect the proxy's + // X-Forwarded-Proto, but if that header is ever missing or a proxy + // is misconfigured, this still forces https:// asset/route URLs on + // any environment whose APP_URL is itself https — so a plain-http + // request never causes a mixed-content-blocked asset again. + if (str(config('app.url'))->startsWith('https://')) { + URL::forceScheme('https'); + } } /** diff --git a/bootstrap/app.php b/bootstrap/app.php index e5f0d6c..997be18 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -11,6 +11,7 @@ use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; use Modules\Identity\Http\Middleware\AuthenticateSanctumOrFastApiJwt; use Modules\Identity\Http\Middleware\EnsureFastApiAgent; +use Symfony\Component\HttpFoundation\Request as SymfonyRequest; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -24,6 +25,22 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { + // Staging/production sit behind a reverse proxy/load balancer that + // terminates SSL — without this, Laravel never sees the original + // request as HTTPS, so it generates http:// asset URLs, which + // browsers then block as mixed content on the https:// page (e.g. + // Filament's file-upload.js failing to load, breaking that field's + // JS-enhanced dropzone). Trusting '*' is the standard Laravel + // pattern when the proxy's IP isn't fixed/known in advance. + $middleware->trustProxies( + at: '*', + headers: SymfonyRequest::HEADER_X_FORWARDED_FOR + | SymfonyRequest::HEADER_X_FORWARDED_HOST + | SymfonyRequest::HEADER_X_FORWARDED_PORT + | SymfonyRequest::HEADER_X_FORWARDED_PROTO + | SymfonyRequest::HEADER_X_FORWARDED_AWS_ELB, + ); + $middleware->alias([ 'fastapi.agent' => EnsureFastApiAgent::class, 'api.auth' => AuthenticateSanctumOrFastApiJwt::class,