Reverse Proxy

When running Windshift behind a reverse proxy, configure Windshift to trust forwarded headers and restrict allowed hostnames.

Required Settings

./windshift --use-proxy --allowed-hosts windshift.example.com --base-url https://windshift.example.com

Or via environment variables:

export USE_PROXY=true
export ALLOWED_HOSTS=windshift.example.com
export BASE_URL=https://windshift.example.com
./windshift
Flag Env Var Description
--use-proxy USE_PROXY Trust X-Forwarded-Proto and X-Forwarded-For headers
--allowed-hosts ALLOWED_HOSTS Comma-separated list of valid hostnames
--base-url BASE_URL Public URL used to generate links in emails, SSO redirects, and calendar feeds
--allowed-port - Port used for CORS and WebAuthn origin validation
--additional-proxies ADDITIONAL_PROXIES Additional trusted proxy IPs beyond the default private ranges

Note: BASE_URL should be set to the public URL users access Windshift from (e.g. https://windshift.example.com). Without it, email verification links, magic login links, SSO redirects, and calendar feed URLs will point at http://localhost:8080.

Important: When --use-proxy is enabled, Windshift trusts X-Forwarded-For and X-Forwarded-Proto headers to determine the client's real IP address and protocol. These headers can be spoofed by any client. You must ensure that Windshift is not directly reachable from the internet - only the reverse proxy should be able to connect to Windshift's port. Bind Windshift to 127.0.0.1 or use firewall rules / Docker networking to block external access. Otherwise, an attacker can forge these headers to bypass IP-based restrictions or trick Windshift into thinking a connection is over HTTPS when it isn't.

Caddy

Caddy provides automatic HTTPS with minimal configuration:

windshift.example.com {
    reverse_proxy localhost:8080
}

Caddy automatically handles TLS certificates via Let's Encrypt and sets the correct forwarded headers.

Nginx

server {
    listen 443 ssl http2;
    server_name windshift.example.com;

    ssl_certificate     /etc/ssl/certs/windshift.pem;
    ssl_certificate_key /etc/ssl/private/windshift.key;

    location / {
        proxy_pass http://localhost:8080;
        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;

        # WebSocket support (for live updates)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Traefik

Using Docker labels:

services:
  windshift:
    image: ghcr.io/windshiftapp/windshift:latest
    environment:
      - USE_PROXY=true
      - ALLOWED_HOSTS=${DOMAIN}
      - BASE_URL=https://${DOMAIN}
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.windshift.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.windshift.entrypoints=websecure"
      - "traefik.http.routers.windshift.tls.certresolver=letsencrypt"
      - "traefik.http.services.windshift.loadbalancer.server.port=8080"

  traefik:
    image: traefik:v3.4
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - letsencrypt-data:/letsencrypt

TLS Termination

If you prefer to terminate TLS at Windshift itself (without a reverse proxy), use the built-in TLS support:

./windshift --tls-cert /path/to/cert.pem --tls-key /path/to/key.pem

This is useful for simple deployments where an external proxy isn't needed.