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.comOr 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_URLshould 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 athttp://localhost:8080.
Important: When
--use-proxyis enabled, Windshift trustsX-Forwarded-ForandX-Forwarded-Protoheaders 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 to127.0.0.1or 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:/letsencryptTLS 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.pemThis is useful for simple deployments where an external proxy isn't needed.