Compare commits

..

2 Commits

Author SHA1 Message Date
Zimeng Xiong e9d349bb0e fix express proxy headers 2026-01-30 14:28:38 -08:00
Zimeng Xiong 6a84cc4ab7 repro issue 2026-01-30 14:19:24 -08:00
6 changed files with 13 additions and 19 deletions
+1 -4
View File
@@ -120,17 +120,14 @@ docker compose up -d
When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly: When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly:
- `FRONTEND_URL` (backend) must match the public URL that users hit (e.g. `https://excalidash.example.com`). This controls CORS and Socket.IO origin checks. **Supports multiple comma-separated URLs** for accessing from different addresses. - `FRONTEND_URL` (backend) must match the public URL that users hit (e.g. `https://excalidash.example.com`). This controls CORS and Socket.IO origin checks.
- `BACKEND_URL` (frontend) tells the Nginx container how to reach the backend from inside Docker/Kubernetes. Override it if your reverse proxy exposes the backend under a different hostname. - `BACKEND_URL` (frontend) tells the Nginx container how to reach the backend from inside Docker/Kubernetes. Override it if your reverse proxy exposes the backend under a different hostname.
```yaml ```yaml
# docker-compose.yml example # docker-compose.yml example
backend: backend:
environment: environment:
# Single URL
- FRONTEND_URL=https://excalidash.example.com - FRONTEND_URL=https://excalidash.example.com
# Or multiple URLs (comma-separated) for local + network access
# - FRONTEND_URL=http://localhost:6767,http://192.168.1.100:6767,http://nas.local:6767
frontend: frontend:
environment: environment:
# For standard Docker Compose (default) # For standard Docker Compose (default)
+1 -1
View File
@@ -1 +1 @@
0.3.2 0.3.1
+5 -5
View File
@@ -1,12 +1,12 @@
{ {
"name": "backend", "name": "backend",
"version": "0.3.2", "version": "0.3.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "backend", "name": "backend",
"version": "0.3.2", "version": "0.3.1",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@prisma/client": "^5.22.0", "@prisma/client": "^5.22.0",
@@ -3286,9 +3286,9 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.23", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "backend", "name": "backend",
"version": "0.3.2", "version": "0.3.1",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -51,13 +51,12 @@ describe("Issue #38: CSRF with trust proxy settings", () => {
.set("X-Forwarded-For", "203.0.113.42, 10.0.0.5, 172.17.0.3") .set("X-Forwarded-For", "203.0.113.42, 10.0.0.5, 172.17.0.3")
.set("User-Agent", "Mozilla/5.0 Test"); .set("User-Agent", "Mozilla/5.0 Test");
// With trust proxy: 1 in supertest (no real socket), Express takes the last IP // With trust proxy: 1, Express takes second-to-last IP (the external proxy)
// In production with a real connection, behavior differs - the key point is it's NOT the client IP expect(response1.body.ip).toBe("10.0.0.5");
expect(response1.body.ip).toBe("172.17.0.3");
console.log( console.log(
"trust proxy: 1 → IP:", "trust proxy: 1 → IP:",
response1.body.ip, response1.body.ip,
"(not the real client IP)", "(external proxy IP - WRONG)",
); );
// With trust proxy: true // With trust proxy: true
@@ -161,12 +160,10 @@ describe("Issue #38: CSRF with trust proxy settings", () => {
}); });
// Client -> Synology (192.168.1.x) -> Docker frontend (192.168.11.x) -> Backend // Client -> Synology (192.168.1.x) -> Docker frontend (192.168.11.x) -> Backend
// In supertest without real socket, trust proxy: 1 returns last IP
// Key point: it's NOT the real client IP (192.168.0.100)
await request(app) await request(app)
.get("/test") .get("/test")
.set("X-Forwarded-For", "192.168.0.100, 192.168.1.4, 192.168.11.166"); .set("X-Forwarded-For", "192.168.0.100, 192.168.1.4, 192.168.11.166");
console.log(" With trust proxy: 1, Express sees:", seenIp); console.log(" With trust proxy: 1, Express sees:", seenIp);
expect(seenIp).toBe("192.168.11.166"); // Not the real client IP expect(seenIp).toBe("192.168.1.4"); // Proxy IP, not client IP
}); });
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "frontend", "name": "frontend",
"private": true, "private": true,
"version": "0.3.2", "version": "0.3.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",