From 9113501906d46dddc4608f54acbb8e8e0f7c6a15 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 12 Jun 2026 19:58:16 -0400 Subject: [PATCH] Add `PROXY_HEADERS_TRUSTED` env (#12676) * Add PROXY_HEADERS_TRUSTED env * Don't trust X-Forwarded-Proto for HTTPS redirect when proxy headers untrusted --------- Co-authored-by: Claude Opus 4.8 --- .env.sample | 5 +++++ server/env.ts | 10 ++++++++++ server/services/web.ts | 21 ++++++++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.env.sample b/.env.sample index 30e1044124..19136f8aae 100644 --- a/.env.sample +++ b/.env.sample @@ -140,6 +140,11 @@ FORCE_HTTPS=true # and "X-Client-IP". # PROXY_IP_HEADER= +# Whether to trust the X-Forwarded-* headers (e.g. X-Forwarded-For, +# X-Forwarded-Proto) set by an upstream proxy. Set to false if not +# running behind a proxy in production. +# PROXY_HEADERS_TRUSTED=true + # –––––––––––––––––––––––––––––––––––––– # –––––––––– AUTHENTICATION –––––––––– diff --git a/server/env.ts b/server/env.ts index 463ae6af8a..a00ee84a46 100644 --- a/server/env.ts +++ b/server/env.ts @@ -372,6 +372,16 @@ export class Environment { @IsOptional() public PROXY_IP_HEADER = this.toOptionalString(environment.PROXY_IP_HEADER); + /** + * Whether to trust the X-Forwarded-* headers (e.g. X-Forwarded-For, + * X-Forwarded-Proto) set by an upstream proxy or load balancer. Defaults to + * true for backwards compat. Set to false if not running behind a proxy in production. + */ + @IsBoolean() + public PROXY_HEADERS_TRUSTED = this.toBoolean( + environment.PROXY_HEADERS_TRUSTED ?? "true" + ); + /** * Should the installation send anonymized statistics to the maintainers. * Defaults to true. diff --git a/server/services/web.ts b/server/services/web.ts index a650c4b40c..057d141c26 100644 --- a/server/services/web.ts +++ b/server/services/web.ts @@ -29,6 +29,16 @@ export default function init(app: Koa = new Koa(), server?: Server) { void initI18n(); if (env.isProduction) { + // Trust the X-Forwarded-* headers set by an upstream proxy, eg + // X-Forwarded-For. Defaults to true, but can be disabled with + // PROXY_HEADERS_TRUSTED when the app is reachable directly. + if (env.PROXY_HEADERS_TRUSTED) { + app.proxy = true; + if (env.PROXY_IP_HEADER) { + app.proxyIpHeader = env.PROXY_IP_HEADER; + } + } + // Force redirect to HTTPS protocol unless explicitly disabled if (env.FORCE_HTTPS) { app.use( @@ -37,19 +47,16 @@ export default function init(app: Koa = new Koa(), server?: Server) { if (httpsResolver(ctx)) { return true; } - return xForwardedProtoResolver(ctx); + // Only honor X-Forwarded-Proto when proxy headers are trusted + return env.PROXY_HEADERS_TRUSTED + ? xForwardedProtoResolver(ctx) + : false; }, }) ); } else { Logger.warn("Enforced https was disabled with FORCE_HTTPS env variable"); } - - // trust header fields set by our proxy. eg X-Forwarded-For - app.proxy = true; - if (env.PROXY_IP_HEADER) { - app.proxyIpHeader = env.PROXY_IP_HEADER; - } } // Make `ctx.userAgent` available