Reverse Proxy
When you run Windshift behind a reverse proxy, configure it to trust forwarded headers and set its browser-security origins.
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 browser origins for CORS, CSRF, WebAuthn, and SSO redirect validation. It does not filter Host headers. Leave it unset for one public origin to derive it from BASE_URL. |
--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: Set
BASE_URLto the public URL users use to access Windshift, such ashttps://windshift.example.com. Without it, Windshift points email links, SSO redirects, and calendar feeds tohttp://localhost:8080.
Important: When you enable
--use-proxy, Windshift trusts theX-Forwarded-ForandX-Forwarded-Protoheaders. Any client can spoof these headers. Make sure only the reverse proxy can connect to the Windshift port. Bind Windshift to127.0.0.1, or use firewall rules or Docker networking to block external access. Otherwise, an attacker can forge headers to bypass IP restrictions or make Windshift treat an HTTP connection as HTTPS.
Caddy
Use this Caddy configuration for automatic HTTPS:
windshift.example.com {
reverse_proxy localhost:8080
}Caddy gets TLS certificates from Let's Encrypt and sets the required 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;
# Server-Sent Events (live updates)
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 3600s;
# Match Windshift's 32 MiB multipart upload limit.
client_max_body_size 32m;
}
}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.0
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:/letsencryptMobile app (PWA) assets
The Windshift mobile app at /m is a Progressive Web App. The browser must receive two root assets unchanged. They enable installation and push notifications:
GET /manifest.webmanifest, served asapplication/manifest+jsonGET /service-worker.js, served astext/javascriptwith aService-Worker-Allowed: /header
Most proxies pass these assets through with the configurations above. Avoid these common mistakes:
- Do not rewrite or override their
Content-Type. The browser rejects a service worker with the wrong content type. - Do not strip the
Service-Worker-Allowedheader. - Do not add aggressive caching for these paths. Windshift sends
Cache-Control: no-cacheso clients get new versions. A caching proxy can keep users on a stale app.
If you proxy specific paths instead of /, include /m, /manifest.webmanifest, and /service-worker.js.
Subpath deployments
To serve Windshift under a path prefix, set the prefix in BASE_URL and WINDSHIFT_CONTEXT_PATH. For example, use https://example.com/windshift/:
export BASE_URL=https://example.com/windshift
export WINDSHIFT_CONTEXT_PATH=/windshift
export USE_PROXY=trueForward the prefix to Windshift and do not strip it before proxying. With Nginx:
location /windshift/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /windshift;
# Server-Sent Events (live updates)
proxy_http_version 1.1;
proxy_buffering off;
proxy_read_timeout 3600s;
client_max_body_size 32m;
}Keep BASE_URL and WINDSHIFT_CONTEXT_PATH in agreement. Windshift derives generated links, SSO redirects, mobile PWA paths, and asset URLs from them.
TLS Termination
If Windshift terminates TLS without a reverse proxy, use its built-in TLS support:
./windshift --tls-cert /path/to/cert.pem --tls-key /path/to/key.pemUse this option for a simple deployment that does not need an external proxy.