From 0320d6a5b6b181a174fdf3cd8231c732bd66a1b8 Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Tue, 3 Mar 2026 12:37:06 +0100
Subject: [PATCH] chore: prepare for PR
---
app/Http/Middleware/TrustHosts.php | 7 +++
resources/views/errors/419.blade.php | 10 ++++-
tests/Feature/TrustHostsMiddlewareTest.php | 50 ++++++++++++++++++++++
3 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php
index f0b9d67f2..5fca583d9 100644
--- a/app/Http/Middleware/TrustHosts.php
+++ b/app/Http/Middleware/TrustHosts.php
@@ -91,6 +91,13 @@ public function hosts(): array
// Trust all subdomains of APP_URL as fallback
$trustedHosts[] = $this->allSubdomainsOfApplicationUrl();
+ // Always trust loopback addresses so local access works even when FQDN is configured
+ foreach (['localhost', '127.0.0.1', '[::1]'] as $localHost) {
+ if (! in_array($localHost, $trustedHosts, true)) {
+ $trustedHosts[] = $localHost;
+ }
+ }
+
return array_filter($trustedHosts);
}
}
diff --git a/resources/views/errors/419.blade.php b/resources/views/errors/419.blade.php
index 8569f4e22..70b5d7a92 100644
--- a/resources/views/errors/419.blade.php
+++ b/resources/views/errors/419.blade.php
@@ -5,7 +5,15 @@
This page is definitely old, not like you!
Your session has expired. Please log in again to continue.
-
+
+ Using a reverse proxy or Cloudflare Tunnel?
+
+ - Set your domain in Settings → FQDN to match the URL you use to access Coolify.
+ - Cloudflare users: disable Browser Integrity Check and Under Attack Mode for your Coolify domain, as these can interrupt login sessions.
+ - If you can still access Coolify via
localhost, log in there first to configure your FQDN.
+
+
+
Back to Login
diff --git a/tests/Feature/TrustHostsMiddlewareTest.php b/tests/Feature/TrustHostsMiddlewareTest.php
index b745259fe..5c60b30d6 100644
--- a/tests/Feature/TrustHostsMiddlewareTest.php
+++ b/tests/Feature/TrustHostsMiddlewareTest.php
@@ -286,6 +286,56 @@
expect($response->status())->not->toBe(400);
});
+it('trusts localhost when FQDN is configured', function () {
+ InstanceSettings::updateOrCreate(
+ ['id' => 0],
+ ['fqdn' => 'https://coolify.example.com']
+ );
+
+ $middleware = new TrustHosts($this->app);
+ $hosts = $middleware->hosts();
+
+ expect($hosts)->toContain('localhost');
+});
+
+it('trusts 127.0.0.1 when FQDN is configured', function () {
+ InstanceSettings::updateOrCreate(
+ ['id' => 0],
+ ['fqdn' => 'https://coolify.example.com']
+ );
+
+ $middleware = new TrustHosts($this->app);
+ $hosts = $middleware->hosts();
+
+ expect($hosts)->toContain('127.0.0.1');
+});
+
+it('trusts IPv6 loopback when FQDN is configured', function () {
+ InstanceSettings::updateOrCreate(
+ ['id' => 0],
+ ['fqdn' => 'https://coolify.example.com']
+ );
+
+ $middleware = new TrustHosts($this->app);
+ $hosts = $middleware->hosts();
+
+ expect($hosts)->toContain('[::1]');
+});
+
+it('allows local access via localhost when FQDN is configured and request uses localhost host header', function () {
+ InstanceSettings::updateOrCreate(
+ ['id' => 0],
+ ['fqdn' => 'https://coolify.example.com']
+ );
+
+ $response = $this->get('/', [
+ 'Host' => 'localhost',
+ ]);
+
+ // Should NOT be rejected as untrusted host (would be 400)
+ expect($response->status())->not->toBe(400);
+});
+
it('skips host validation for webhook endpoints', function () {
// All webhook routes are under /webhooks/* prefix (see RouteServiceProvider)
// and use cryptographic signature validation instead of host validation