diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index bb2687083..c2a2cb41a 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -19,6 +19,7 @@ public function hosts(): array $trustedHosts = []; // Trust the configured FQDN from InstanceSettings (cached to avoid DB query on every request) + // Use empty string as sentinel value instead of null so negative results are cached $fqdnHost = Cache::remember('instance_settings_fqdn_host', 300, function () { try { $settings = InstanceSettings::get(); @@ -26,16 +27,19 @@ public function hosts(): array $url = Url::fromString($settings->fqdn); $host = $url->getHost(); - return $host ?: null; + return $host ?: ''; } } catch (\Exception $e) { // If instance settings table doesn't exist yet (during installation), - // return null to fall back to APP_URL only + // return empty string (sentinel) so this result is cached } - return null; + return ''; }); + // Convert sentinel value back to null for consumption + $fqdnHost = $fqdnHost !== '' ? $fqdnHost : null; + if ($fqdnHost) { $trustedHosts[] = $fqdnHost; } diff --git a/tests/Feature/TrustHostsMiddlewareTest.php b/tests/Feature/TrustHostsMiddlewareTest.php index 7c02aa7e9..f875a235e 100644 --- a/tests/Feature/TrustHostsMiddlewareTest.php +++ b/tests/Feature/TrustHostsMiddlewareTest.php @@ -200,3 +200,30 @@ expect($hosts1)->toBe($hosts2); expect($hosts2)->toContain('coolify.example.com'); }); + +it('caches negative results when no FQDN is configured', function () { + // Create instance settings without FQDN + InstanceSettings::updateOrCreate( + ['id' => 0], + ['fqdn' => null] + ); + + // Clear cache first + Cache::forget('instance_settings_fqdn_host'); + + // First call - should query database and cache empty string sentinel + $middleware1 = new TrustHosts($this->app); + $hosts1 = $middleware1->hosts(); + + // Verify empty string sentinel is cached (not null, which wouldn't be cached) + expect(Cache::has('instance_settings_fqdn_host'))->toBeTrue(); + expect(Cache::get('instance_settings_fqdn_host'))->toBe(''); + + // Subsequent calls should use cached sentinel value + $middleware2 = new TrustHosts($this->app); + $hosts2 = $middleware2->hosts(); + + expect($hosts1)->toBe($hosts2); + // Should only contain APP_URL pattern, not any FQDN + expect($hosts2)->not->toBeEmpty(); +});