feat: add trust_forwarded_proto option for SSL redirect handling in reverse proxy scenarios

When Nginx is behind another proxy server (like CloudFlare or AWS ALB), the force-SSL
feature can cause redirect loops because Nginx sees the connection as plain HTTP
while SSL is already handled upstream. This adds a new boolean option to trust
the X-Forwarded-Proto header from upstream proxies.

Changes:
- Add `trust_forwarded_proto` column to proxy_host table (migration)
- Update model and API schema to support the new boolean field
- Modify force-ssl Nginx template to check X-Forwarded-Proto/X-Forwarded-Scheme
- Add map directives in nginx.conf to validate and sanitize forwarded headers
- Add advanced option toggle in frontend UI with i18n support (EN/ZH)
- Set proxy headers from validated map variables instead of $scheme

This allows administrators to control SSL redirect behavior when Nginx is deployed
behind a TLS-terminating proxy.
This commit is contained in:
jerry-yuan
2026-01-31 13:11:47 +00:00
parent bad3eac515
commit 187d21a0d5
15 changed files with 120 additions and 4 deletions

View File

@@ -0,0 +1,31 @@
import { migrate as logger } from "../logger.js";
const migrateName = "redirect_auto_scheme";
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @returns {Promise}
*/
const up = function (knex) {
return knex.schema.alterTable('proxy_host', (table) => {
table.tinyint('trust_forwarded_proto').notNullable().defaultTo(0);
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @returns {Promise}
*/
const down = function (knex) {
return knex.schema.alterTable('proxy_host', (table) => {
table.dropColumn('trust_forwarded_proto');
});
};
export { up, down };

View File

@@ -21,6 +21,7 @@ const boolFields = [
"enabled",
"hsts_enabled",
"hsts_subdomains",
"trust_forwarded_proto",
];
class ProxyHost extends Model {

View File

@@ -22,7 +22,8 @@
"enabled",
"locations",
"hsts_enabled",
"hsts_subdomains"
"hsts_subdomains",
"trust_forwarded_proto"
],
"properties": {
"id": {
@@ -141,6 +142,10 @@
"hsts_subdomains": {
"$ref": "../common.json#/properties/hsts_subdomains"
},
"trust_forwarded_proto":{
"type": "boolean",
"example": false
},
"certificate": {
"oneOf": [
{

View File

@@ -56,6 +56,9 @@
"hsts_subdomains": {
"$ref": "../../../../components/proxy-host-object.json#/properties/hsts_subdomains"
},
"trust_forwarded_proto": {
"$ref": "../../../../components/proxy-host-object.json#/properties/trust_forwarded_proto"
},
"http2_support": {
"$ref": "../../../../components/proxy-host-object.json#/properties/http2_support"
},

View File

@@ -48,6 +48,9 @@
"hsts_subdomains": {
"$ref": "../../../components/proxy-host-object.json#/properties/hsts_subdomains"
},
"trust_forwarded_proto": {
"$ref": "../../../components/proxy-host-object.json#/properties/trust_forwarded_proto"
},
"http2_support": {
"$ref": "../../../components/proxy-host-object.json#/properties/http2_support"
},

View File

@@ -1,6 +1,11 @@
{% if certificate and certificate_id > 0 -%}
{% if ssl_forced == 1 or ssl_forced == true %}
# Force SSL
{% if trust_forwarded_proto == true %}
set $trust_forwarded_proto "T";
{% else %}
set $trust_forwarded_proto "F";
{% endif %}
include conf.d/include/force-ssl.conf;
{% endif %}
{% endif %}