From 38dfc45e2bda1298a3365d4c35fca11ddfbd1d8d Mon Sep 17 00:00:00 2001 From: Juri Ehret Date: Fri, 15 May 2026 09:32:30 +0200 Subject: [PATCH] fix(url): trust X-Forwarded-Proto for site URL scheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both Site::detectSiteUrl and Editor::detectSiteUrl looked only at $_SERVER['HTTPS'], which is unset when PHP sits behind a TLS-terminating reverse proxy (nginx-proxy, traefik, …). Result: every absolute URL the templates emit (asset hrefs, form actions, navigation links) was http:// even though the page itself was served over https — every modern browser blocks the assets as Mixed Content. Now both check HTTP_X_FORWARDED_PROTO first, falling back to the $_SERVER['HTTPS'] heuristic. First value wins for chained proxies ('https,http') so a downstream http hop can't downgrade the scheme. Verified by hitting https://demos.scriptor-cms.dev (nginx-proxy + acme-companion on Hetzner) — no more Mixed Content warnings. --- boot/Editor/Editor.php | 8 +++++++- boot/Frontend/Site.php | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/boot/Editor/Editor.php b/boot/Editor/Editor.php index 41bfbec..0ede34e 100644 --- a/boot/Editor/Editor.php +++ b/boot/Editor/Editor.php @@ -247,7 +247,13 @@ private static function editorSegments(array $config): UrlSegments private static function detectSiteUrl(): string { - $scheme = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; + // Prefer X-Forwarded-Proto when present — TLS-terminating reverse + // proxies (nginx-proxy, traefik, …) reach PHP over plain HTTP + // and signal the original scheme via this header. First value + // wins for chained proxies ("https,http"). + $proto = $_SERVER['HTTP_X_FORWARDED_PROTO'] + ?? ((! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'); + $scheme = strtolower(trim(explode(',', $proto)[0])) === 'https' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; return $scheme . '://' . $host; } diff --git a/boot/Frontend/Site.php b/boot/Frontend/Site.php index 7e56316..26fdffb 100644 --- a/boot/Frontend/Site.php +++ b/boot/Frontend/Site.php @@ -367,7 +367,13 @@ protected function renderNavigation(): string private static function detectSiteUrl(): string { - $scheme = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; + // Prefer X-Forwarded-Proto when present — TLS-terminating reverse + // proxies (nginx-proxy, traefik, …) reach PHP over plain HTTP + // and signal the original scheme via this header. First value + // wins for chained proxies ("https,http"). + $proto = $_SERVER['HTTP_X_FORWARDED_PROTO'] + ?? ((! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'); + $scheme = strtolower(trim(explode(',', $proto)[0])) === 'https' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; return $scheme . '://' . $host; }