How to Configure Nginx as a Reverse Proxy for TREK

Configure Nginx to terminate TLS, forward traffic to TREK's Node.js server on port 3000, and proxy WebSocket upgrades at /ws while setting X-Forwarded-Proto headers so TREK correctly handles HTTPS redirects and URL generation.

TREK is a self-hosted travel planner that exposes a Node.js application on port 3000. To deploy it securely in production, you must configure Nginx as a reverse proxy for TREK to handle TLS termination, client IP preservation, and WebSocket connections for real-time collaboration features.

Prerequisites for Nginx Reverse Proxy Configuration

Before editing your Nginx configuration, ensure you have:

  • A running TREK instance accessible on localhost:3000 (the default Docker Compose exposes this port)
  • SSL certificates for your domain (e.g., from Let's Encrypt at /etc/ssl/fullchain.pem and /etc/ssl/privkey.pem)
  • Nginx installed on the host or in a container with network access to the TREK service

Complete Nginx Configuration for TREK

The official configuration documented in the repository's README.md splits traffic into two server blocks: one for HTTP redirection and one for HTTPS termination.

HTTP to HTTPS Redirect

Force all cleartext traffic to the secure endpoint to ensure session cookies and travel data remain encrypted:

server {
    listen 80;
    server_name trek.yourdomain.com;
    return 301 https://$host$request_uri;
}

TLS Termination and Upstream Proxy

The HTTPS server block handles TLS certificates, upload size limits, and proxy headers required by TREK's middleware:

server {
    listen 443 ssl http2;
    server_name trek.yourdomain.com;

    ssl_certificate     /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/privkey.pem;

    # 500 MB covers backup-restore uploads (capped at 500 MB server-side)

    client_max_body_size 500m;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

The X-Forwarded-Proto header is critical because TREK's globalMiddleware.ts (lines 27-30) reads this to determine the original request protocol, enabling the optional FORCE_HTTPS redirect logic and correct absolute URL generation.

WebSocket Support for Real-Time Collaboration

TREK uses WebSockets for live collaboration on the /ws endpoint. Nginx must forward the Upgrade and Connection headers and disable the default 60-second read timeout:

location /ws {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400;
}

Without this block, the collaborative editing features will fail to connect because the proxy will treat the WebSocket handshake as a standard HTTP request.

Configuring TREK to Trust the Proxy

TREK must explicitly trust the Nginx reverse proxy to interpret X-Forwarded-* headers correctly. Set the TRUST_PROXY environment variable in your Docker Compose configuration:

services:
  app:
    image: mauriceboe/trek:latest
    environment:
      - NODE_ENV=production
      - PORT=3000
      - TRUST_PROXY=1  # Tell TREK there is one trusted proxy (Nginx)

      # - FORCE_HTTPS=true  # Optional: let TREK handle redirects (Nginx recommended instead)

In server/src/middleware/globalMiddleware.ts, the application reads TRUST_PROXY and executes app.set('trust proxy', process.env.TRUST_PROXY || 1). This enables req.secure and req.protocol to reflect the original client scheme forwarded by Nginx, ensuring secure cookie settings and correct URL generation in server/src/services/cookie.ts and server/src/services/shareService.ts.

Docker Compose Deployment Example

Place the following docker-compose.yml alongside your Nginx configuration to ensure TREK binds to the expected port and respects the reverse proxy headers:

version: '3.8'
services:
  app:
    image: mauriceboe/trek:latest
    container_name: trek
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
      - ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - TRUST_PROXY=1
    volumes:
      - ./data:/app/data
      - ./uploads:/app/uploads
    restart: unless-stopped

Ensure your Nginx proxy_pass points to http://localhost:3000 (or the container's IP if Nginx runs in Docker) and that the TRUST_PROXY value matches the number of proxy layers (typically 1 for a single Nginx instance).

Testing Your Reverse Proxy Setup

Verify the configuration works end-to-end:

  1. Test HTTP to HTTPS redirect:
curl -I http://trek.yourdomain.com

You should see Location: https://trek.yourdomain.com/.

  1. Test WebSocket connectivity:
wscat -c ws://trek.yourdomain.com/ws

The connection should open without error if the /ws location block is configured correctly.

  1. Verify TREK recognizes HTTPS:

Check the browser network tab or server logs to confirm that req.secure returns true when accessing via HTTPS, indicating the X-Forwarded-Proto header is being honored.

Summary

  • Terminate TLS at Nginx using listen 443 ssl http2 and forward to port 3000, offloading cryptography from the Node.js process.
  • Preserve proxy headers including X-Forwarded-Proto so TREK's globalMiddleware.ts can correctly identify secure connections and generate absolute URLs.
  • Enable WebSocket support by adding a specific /ws location block with Upgrade and Connection headers, plus an extended proxy_read_timeout.
  • Set TRUST_PROXY=1 in your Docker Compose environment to align with the trust proxy logic in server/src/middleware/globalMiddleware.ts.
  • Allow large uploads by setting client_max_body_size 500m to accommodate TREK's 500 MB backup restore functionality.

Frequently Asked Questions

Why does TREK need the X-Forwarded-Proto header?

TREK relies on the X-Forwarded-Proto header to determine the original request scheme when running behind a reverse proxy. According to server/src/middleware/globalMiddleware.ts, the application sets trust proxy based on the TRUST_PROXY environment variable, allowing req.secure and req.protocol to reflect the client's original connection rather than the internal HTTP proxy link. This enables the optional FORCE_HTTPS redirect and ensures secure cookie attributes are set correctly.

What happens if I don't configure the /ws location block in Nginx?

Without the specific /ws configuration, Nginx will treat WebSocket upgrade requests as standard HTTP traffic, causing the connection to fail after the 60-second default timeout. TREK's real-time collaboration features require persistent WebSocket connections to the /ws endpoint, so you must forward the Upgrade and Connection headers and set proxy_read_timeout to a high value (such as 86400 seconds) to prevent timeouts during idle periods.

How does client_max_body_size affect TREK functionality?

TREK allows users to upload trip backups up to 500 MB. If Nginx's default client_max_body_size (1 MB) is not increased, the reverse proxy will return a 413 Request Entity Too Large error before the request reaches TREK. Setting client_max_body_size 500m in the Nginx server block ensures large file uploads reach the application server successfully.

Can I run Nginx in a Docker container alongside TREK?

Yes, you can run Nginx in a separate container or use a reverse proxy network. Ensure that Nginx can reach the TREK container via the internal Docker network (e.g., http://trek:3000 instead of localhost:3000) and that the TRUST_PROXY count accounts for any additional proxy layers. If Nginx is the only reverse proxy, setting TRUST_PROXY=1 remains correct regardless of whether Nginx runs on the host or in a container.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →