Operations
MarkdownReverse proxy cookbook
Operator cookbook for TLS termination and WebSocket upgrades in front of Externa (Docker Compose or bare metal).
Related
App env (always)
APP_URL=https://externa.example.com
TRUSTED_PROXIES=*
# or comma-separated proxy CIDRs
SESSION_SECURE_COOKIE=true
Browser-facing Reverb (baked into the Vite bundle — rebuild image after changes):
REVERB_HOST=externa.example.com
REVERB_PORT=443
REVERB_SCHEME=https
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
Passkeys need a secure context: HTTPS APP_URL matching Fortify WebAuthn relying party — Passkeys.
Compose defaults: app published APP_PORT→80 (prod sample often 8080), Reverb REVERB_HOST_PORT→8080 (often host 8081). Proxy to those upstreams (or to service DNS app:80 / reverb:8080 on the Compose network).
Caddy
externa.example.com {
encode gzip
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
# Reverb WebSockets (path depends on your Echo setup — default Laravel Reverb)
handle /app/* {
reverse_proxy reverb:8080
}
handle {
reverse_proxy app:80
}
}
If Echo connects on a dedicated host/port, point a second site block at reverb:8080 with TLS.
Traefik (Docker labels sketch)
services:
app:
labels:
- traefik.enable=true
- traefik.http.routers.externa.rule=Host(`externa.example.com`)
- traefik.http.routers.externa.entrypoints=websecure
- traefik.http.routers.externa.tls.certresolver=le
- traefik.http.services.externa.loadbalancer.server.port=80
reverb:
labels:
- traefik.enable=true
- traefik.http.routers.externa-ws.rule=Host(`externa.example.com`) && PathPrefix(`/app`)
- traefik.http.routers.externa-ws.entrypoints=websecure
- traefik.http.routers.externa-ws.tls.certresolver=le
- traefik.http.services.externa-ws.loadbalancer.server.port=8080
Ensure Traefik forwards X-Forwarded-* (default) so Laravel trusted proxies work with TRUSTED_PROXIES.
nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name externa.example.com;
# ssl_certificate …;
# ssl_certificate_key …;
location /app/ {
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s; # long AI / SSE if needed
}
}
Adjust upstream ports to your APP_PORT / REVERB_HOST_PORT.
Checklist
- [ ]
APP_URLis HTTPS and matches the browser host - [ ]
TRUSTED_PROXIESset; login/CSRF works behind the proxy - [ ] WebSocket upgrade reaches Reverb; Echo connects (avatar / notifications)
- [ ] Production image rebuilt after
VITE_REVERB_*changes - [ ] Passkeys tested on the public HTTPS origin