--- title: CMS port-leak fix — URL Rewrite serverVariables на host IIS type: concept tags: [iis, url-rewrite, traefik, x-forwarded, asp-net, gotcha] sources: [../sources/iis-host-migration-2026-05-19.md] updated: 2026-05-19 --- # CMS port-leak fix Решение для open issue #1 из [[recovery-architecture-snapshot]] (раньше «X-Forwarded headers не настроены, :4443 leak») и админ-utечки `:8089` обнаруженной 2026-05-19 вечером после attempt 2 host-IIS миграции. Документирует **root cause**, **почему VM работала**, **почему обходной path-B на Windows Docker Desktop невозможен**, и **что в итоге применено**. Связано: [[traefik-on-windows-docker-desktop]] Pitfall 5, [[iis-host-migration-2026-05-19]] Phase 10, [[docker-host-loopback-detect]]. ## Симптом Admin URLs формата (после attempt 2 миграции на host-IIS:8089): - `https://emspb.snolla.com:8089/admin/assets//getList?path=` - `https://emspb.snolla.com:8089/admin/themes/getImageSizes/` - `https://emspb.snolla.com:8089/admin/templates/editors.tmpl.html?v=2.006` Browser HSTS upgrade'ит `http://...:8089` → `https://...:8089` → TCP open, TLS handshake fails (8089 = plain HTTP) → admin SPA ломается. Аналогично, ранее (issue #1) — CMS делал HTTP→HTTPS redirect с `:4443` (traefik external port). ## Root cause `MoreThenCms.Admin\Mis\Web\Mvc\UrlHelpers\UrlHelpers.cs:14-37` `Url.SiteRoot()`: ```csharp var port = context.Request.ServerVariables["SERVER_PORT"]; if (usePort) { if (port == null || port == "80" || port == "443") port = ""; else port = ":" + port; } var protocol = context.Request.ServerVariables["SERVER_PORT_SECURE"]; if (protocol == null || protocol == "0") protocol = "http://"; else protocol = "https://"; var sOut = protocol + context.Request.ServerVariables["SERVER_NAME"] + port + appPath; ``` Читает **socket-level** server variables. На IIS site `snolla` binding `*:8089` HTTP → `SERVER_PORT=8089`, `SERVER_PORT_SECURE=0` → формирует `http://emspb.snolla.com:8089` → рендерится в Razor: ```cshtml @* C:\sites\snolla\Views\Shared\_Layout.cshtml:229 *@ mis.siteRoot = '@Url.SiteRoot().Replace("http:", "https:")' + '/admin'; @* и _LogInLayout.cshtml:156 *@ mis.siteRoot = '@Url.SiteRoot()'; ``` Замена `http:→https:` в Layout была *прошлым* частичным patch'ем — не убирает порт. Десятки .cshtml в admin также используют `Url.SiteRoot()` для inline `