TLS & Reverse Proxy
Caddy (Default)
Section titled “Caddy (Default)”Breeze ships with Caddy as the default reverse proxy. It automatically provisions TLS certificates from Let’s Encrypt.
Production Caddyfile
Section titled “Production Caddyfile”The Caddyfile is generated inline inside docker-compose.yml — there is no separate config file to manage. A reference copy also exists at docker/Caddyfile.prod, but the compose-generated version is what runs in production.
{ email {$ACME_EMAIL}}
{$BREEZE_DOMAIN} { @api path /api/* /health /ready /metrics/* handle @api { encode zstd gzip reverse_proxy api:3001 }
handle { encode zstd gzip reverse_proxy web:4321 }
header { Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" }}How It Works
Section titled “How It Works”- Set
BREEZE_DOMAINandACME_EMAILin your.env.prod - Ensure port 80 and 443 are open and DNS points to your server
- Caddy requests a certificate from Let’s Encrypt on first start
- Certificates auto-renew 30 days before expiry
WebSocket Proxying
Section titled “WebSocket Proxying”Caddy automatically handles WebSocket upgrade for:
- Agent connections at
/api/v1/agents/:id/ws - Terminal sessions at
/api/v1/terminal/:deviceId - Real-time dashboard updates
No additional configuration needed.
Nginx (Alternative)
Section titled “Nginx (Alternative)”If you prefer nginx, here’s an equivalent configuration:
server { listen 80; server_name breeze.yourdomain.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name breeze.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/breeze.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/breeze.yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3;
# Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# API routes location /api/ { proxy_pass http://127.0.0.1:3001; 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 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400s; }
location /health { proxy_pass http://127.0.0.1:3001; }
# Web dashboard location / { proxy_pass http://127.0.0.1:4321; 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; }}Security Headers
Section titled “Security Headers”Both configurations include these security headers:
| Header | Value | Purpose |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | Force HTTPS for 1 year |
X-Content-Type-Options | nosniff | Prevent MIME sniffing |
Referrer-Policy | strict-origin-when-cross-origin | Control referrer information |