From 98dacef556ada5f9a1e9268617f15fb0e3b0efb7 Mon Sep 17 00:00:00 2001 From: Nyan Lin Paing <117423022+LinPaing21@users.noreply.github.com> Date: Sun, 23 Aug 2026 23:19:44 +0700 Subject: [PATCH] Fix asset URLs generated as http:// behind staging's reverse proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Staging terminates SSL at a reverse proxy in front of the app, but Laravel had no trustProxies() configured, so it never saw the request as HTTPS and generated http:// asset URLs on the https:// page. Browsers block that as mixed content, which silently broke every JS-enhanced Filament field (FileUpload, Textarea, etc.) — e.g. the Ev Company logo field falling back to a bare native file input. - bootstrap/app.php: trust the proxy via X-Forwarded-* headers. - AppServiceProvider: force the https scheme when APP_URL is https, as a fallback in case the forwarded header is ever missing. --- app/Providers/AppServiceProvider.php | 11 +++++++++++ bootstrap/app.php | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) 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,