fix(domains): apply noindex to domains that include a port

Closes #11965

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-15 23:20:23 +02:00
co-authored by Claude Opus 5.5
parent 1d03609497
commit 2a202d9fe9
2 changed files with 24 additions and 2 deletions
+6 -2
View File
@@ -7,6 +7,7 @@ use App\Models\Server;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use App\Support\DomainPortOverrides;
use App\Support\ValidationPatterns;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
@@ -765,9 +766,12 @@ function isNoindexDomain(string $domain, ?Collection $noindex_domains): bool
return false;
}
// Flags are stored without a port, so compare portless: https://app.example.com:3000 matches https://app.example.com.
$normalize = fn (string $url) => DomainPortOverrides::withoutPort(ValidationPatterns::normalizeApplicationDomainUrl($url));
return $noindex_domains
->map(fn (string $noindex_domain) => ValidationPatterns::normalizeApplicationDomainUrl($noindex_domain))
->contains(ValidationPatterns::normalizeApplicationDomainUrl($domain));
->map($normalize)
->contains($normalize($domain));
}
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null, bool $is_http_basic_auth_enabled = false, ?string $http_basic_auth_username = null, ?string $http_basic_auth_password = null, ?Collection $noindex_domains = null, bool $is_traffic_analytics_enabled = false, array $domainPortOverrides = [], bool $supports_log_append = false, bool $supports_basic_auth_directive = false)
+18
View File
@@ -155,6 +155,15 @@ describe('Traefik noindex middleware', function () {
expect(middlewaresOf($labels, 'https-0-testuuid'))->toContain('0-testuuid-noindex');
});
test('a port in the configured domain does not silently drop the header', function () {
$labels = traefikLabels(
domains: ['https://app.example.com:3000'],
noindex: ['https://app.example.com'],
);
expect(middlewaresOf($labels, 'https-0-testuuid'))->toContain('0-testuuid-noindex');
});
test('a domain that is not configured is ignored', function () {
$labels = traefikLabels(
domains: ['https://example.com'],
@@ -221,6 +230,15 @@ describe('Caddy noindex header', function () {
expect(collect($labels)->filter(fn ($l) => str_contains($l, 'caddy_0.header.'))->all())->toBeEmpty();
});
test('a port in the configured domain does not silently drop the header', function () {
$labels = caddyLabels(
domains: ['https://app.example.com:3000'],
noindex: ['https://app.example.com'],
);
expect($labels)->toContain('caddy_0.header.1_X-Robots-Tag="noindex, nofollow"');
});
test('no flags means byte-identical output to before the feature', function () {
$domains = ['https://example.com', 'https://other.example.com'];