fix: default TRUST_PROXY to 1 for Docker+reverse-proxy setups (#46)

With the previous default of 'loopback', Express ignored X-Forwarded-Proto
headers from Caddy/nginx when running in Docker (bridge IP, not loopback).
This caused req.secure=false, which made express-session silently drop the
session cookie on login - resulting in a 401 on every subsequent request.

Changing the default to 1 (trust one proxy hop) fixes this for all standard
Docker+reverse-proxy deployments without requiring manual configuration.
This commit is contained in:
Ulas
2026-04-14 09:04:06 +02:00
parent fa1b0d0603
commit 3f387b616e
5 changed files with 22 additions and 6 deletions
+7
View File
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.18.2] - 2026-04-14
### Fixed
- Login failure behind Caddy/nginx reverse proxy in Docker: default `TRUST_PROXY` changed from `'loopback'` to `1` (trust one proxy hop). With `'loopback'`, Express ignored `X-Forwarded-Proto: https` from Caddy (which runs on a Docker bridge IP, not loopback), causing `req.secure = false` and express-session to silently drop the session cookie. The new default of `1` correctly handles any single-proxy setup without requiring manual configuration.
- `docker-compose.yml`: added inline comments explaining reverse proxy vs. direct-access configuration
- `docs/docker-compose.portainer.yml`: added explicit `TRUST_PROXY` variable with default `1`
## [0.18.1] - 2026-04-14 ## [0.18.1] - 2026-04-14
### Added ### Added
+4 -1
View File
@@ -13,7 +13,10 @@ services:
environment: environment:
- NODE_ENV=production - NODE_ENV=production
- DB_PATH=/data/oikos.db - DB_PATH=/data/oikos.db
# Set to false when not using HTTPS/reverse proxy (direct HTTP access) # Reverse proxy setup (Caddy, nginx, Traefik):
# - Remove SESSION_SECURE=false (default is true)
# - TRUST_PROXY is automatically set to 1 (trust one proxy hop)
# Direct HTTP access (no reverse proxy):
- SESSION_SECURE=false - SESSION_SECURE=false
healthcheck: healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"] test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
+4 -1
View File
@@ -31,8 +31,11 @@ services:
- DB_PATH=/data/oikos.db - DB_PATH=/data/oikos.db
- SESSION_SECRET=${SESSION_SECRET:?Set SESSION_SECRET in .env} - SESSION_SECRET=${SESSION_SECRET:?Set SESSION_SECRET in .env}
- DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:?Set DB_ENCRYPTION_KEY in .env} - DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY:?Set DB_ENCRYPTION_KEY in .env}
# Set to true when behind a reverse proxy with HTTPS # Set to true when behind a reverse proxy with HTTPS (Caddy, nginx, Traefik)
- SESSION_SECURE=${SESSION_SECURE:-false} - SESSION_SECURE=${SESSION_SECURE:-false}
# Trust proxy hops (default: 1 for Docker+reverse-proxy setups)
# Set to 'loopback' if running without a reverse proxy
- TRUST_PROXY=${TRUST_PROXY:-1}
# Weather (optional) # Weather (optional)
- OPENWEATHER_API_KEY=${OPENWEATHER_API_KEY:-} - OPENWEATHER_API_KEY=${OPENWEATHER_API_KEY:-}
- OPENWEATHER_CITY=${OPENWEATHER_CITY:-Berlin} - OPENWEATHER_CITY=${OPENWEATHER_CITY:-Berlin}
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "oikos", "name": "oikos",
"version": "0.18.1", "version": "0.18.2",
"description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.",
"main": "server/index.js", "main": "server/index.js",
"type": "module", "type": "module",
+6 -3
View File
@@ -65,9 +65,12 @@ app.use(helmet({
} : false, } : false,
})); }));
// Trust Proxy: nur aktivieren wenn ein Reverse Proxy vorgeschaltet ist (TRUST_PROXY env var). // Trust Proxy: Default 1 = ersten Proxy-Hop vertrauen (korrekt für Caddy/nginx in Docker).
// Default 'loopback' akzeptiert nur X-Forwarded-For von localhost - verhindert IP-Spoofing. // Wird auf 'loopback' gesetzt wenn der Server direkt ohne Reverse-Proxy betrieben wird.
app.set('trust proxy', process.env.TRUST_PROXY || 'loopback'); // Hintergrund: Bei Docker + Caddy/nginx kommt der Request von einer Bridge-IP (z.B. 172.x.x.x),
// nicht von loopback. Mit 'loopback' ignoriert Express das X-Forwarded-Proto-Header von Caddy,
// req.secure bleibt false, und express-session setzt keinen Session-Cookie (Login schlägt fehl).
app.set('trust proxy', process.env.TRUST_PROXY !== undefined ? process.env.TRUST_PROXY : 1);
// -------------------------------------------------------- // --------------------------------------------------------
// Request-Parsing // Request-Parsing