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 <noreply@anthropic.com>
This commit is contained in:
Tom Moor
2026-06-12 19:58:16 -04:00
committed by GitHub
parent 92168c3641
commit 9113501906
3 changed files with 29 additions and 7 deletions
+5
View File
@@ -140,6 +140,11 @@ FORCE_HTTPS=true
# and "X-Client-IP". # and "X-Client-IP".
# PROXY_IP_HEADER= # 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 –––––––––– # –––––––––– AUTHENTICATION ––––––––––
+10
View File
@@ -372,6 +372,16 @@ export class Environment {
@IsOptional() @IsOptional()
public PROXY_IP_HEADER = this.toOptionalString(environment.PROXY_IP_HEADER); 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. * Should the installation send anonymized statistics to the maintainers.
* Defaults to true. * Defaults to true.
+14 -7
View File
@@ -29,6 +29,16 @@ export default function init(app: Koa = new Koa(), server?: Server) {
void initI18n(); void initI18n();
if (env.isProduction) { 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 // Force redirect to HTTPS protocol unless explicitly disabled
if (env.FORCE_HTTPS) { if (env.FORCE_HTTPS) {
app.use( app.use(
@@ -37,19 +47,16 @@ export default function init(app: Koa = new Koa(), server?: Server) {
if (httpsResolver(ctx)) { if (httpsResolver(ctx)) {
return true; return true;
} }
return xForwardedProtoResolver(ctx); // Only honor X-Forwarded-Proto when proxy headers are trusted
return env.PROXY_HEADERS_TRUSTED
? xForwardedProtoResolver(ctx)
: false;
}, },
}) })
); );
} else { } else {
Logger.warn("Enforced https was disabled with FORCE_HTTPS env variable"); 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 // Make `ctx.userAgent` available