Reverse Proxy Setup for Routewarden Dashboard
When deploying Routewarden's security dashboard in production, placing it behind a reverse proxy provides:
- HTTPS / TLS Encryption: Automated certificate renewal with Let's Encrypt or ZeroSSL.
- Custom Domain Routing: Expose the dashboard under
soc.example.comor a subpath like/routewarden. - IP Allowlisting & Zero Trust: Restrict access to corporate VPNs, Tailscale, or Cloudflare Access while keeping edge ingestion endpoints accessible.
- Secure Host Isolation: Bind Routewarden internally to
127.0.0.1:7890so the raw HTTP port is never exposed to the public internet.
Critical Requirements for Live SSE Streaming
Routewarden streams real-time threat telemetry, KPIs, and live event rows to the browser using Server-Sent Events (SSE) at /api/stream.
When routing through any reverse proxy, two settings are essential:
- Disable Proxy Buffering: By default, proxies like NGINX buffer HTTP responses until a buffer (4KB–16KB) fills up before sending it downstream. For SSE streams, this causes events to lag or appear in bursts. Disabling buffering ensures immediate event delivery.
- Long Read Timeouts: SSE maintains a persistent, open HTTP connection. Standard reverse proxies timeout idle connections after 60 seconds unless
proxy_read_timeoutis extended (e.g.86400sor24h).
1. Localhost Isolation (Recommended)
Before configuring your reverse proxy, ensure Routewarden only listens on 127.0.0.1 so external users cannot bypass your proxy:
# Bind dashboard to localhost only so it cannot be accessed directly from WAN:rwarden dashboard up --host 127.0.0.1 --port 7890 --no-open --detach# Verify it is bound only to 127.0.0.1rwarden dashboard status2. Traefik Configuration
Traefik natively supports HTTP/2 and SSE streaming. Set the response buffering middleware to maxResponseBodyBytes: 0 to ensure zero-latency telemetry delivery.
services: dashboard: image: ghcr.io/routewarden/dashboard:latest container_name: routewarden-dashboard restart: unless-stopped command: up --port 7890 --no-open networks: - proxy-net volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ~/.routewarden/dashboard:/root/.routewarden/dashboard labels: - "traefik.enable=true" # Match your dashboard domain - "traefik.http.routers.routewarden.rule=Host(`soc.example.com`)" - "traefik.http.routers.routewarden.entrypoints=websecure" - "traefik.http.routers.routewarden.tls=true" - "traefik.http.routers.routewarden.tls.certresolver=letsencrypt" - "traefik.http.services.routewarden.loadbalancer.server.port=7890" # Disable response buffering for zero-latency SSE streaming - "traefik.http.middlewares.routewarden-buffering.buffering.maxResponseBodyBytes=0" - "traefik.http.routers.routewarden.middlewares=routewarden-buffering"networks: proxy-net: external: trueKey Traefik Directives:
loadbalancer.server.port=7890: Directs traffic to Routewarden's internal HTTP server.responseForwarding.flushInterval: "10ms"/maxResponseBodyBytes: 0: Ensures SSE messages are flushed downstream immediately upon receipt.
3. Caddy Configuration
Caddy automatically provisions TLS certificates and handles HTTP/2 connection multiplexing. Adding flush_interval -1 forces immediate SSE streaming flushes.
# Caddyfile (Subdomain with Automatic Let's Encrypt TLS)soc.example.com { # Reverse proxy to local Routewarden dashboard reverse_proxy localhost:7890 { # Flush SSE chunks immediately for real-time telemetry (< 10ms) flush_interval -1 } # Optional: Enable compression for static assets encode zstd gzip}Key Caddy Directives:
flush_interval -1: Disables output buffering in Caddy'sreverse_proxy, flushing each SSE event packet immediately to the client.handle_path: Strips the/routewardenprefix when routing to subpaths.
4. NGINX Configuration
NGINX is one of the most common production reverse proxies. For Routewarden, disabling buffering and setting long connection timeouts is crucial:
# /etc/nginx/sites-available/soc.example.comserver { listen 80; server_name soc.example.com; return 301 https://$host$request_uri;}server { listen 443 ssl http2; server_name soc.example.com; ssl_certificate /etc/letsencrypt/live/soc.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/soc.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Allow edge node log shipments up to 10MB client_max_body_size 10M; location / { proxy_pass http://127.0.0.1:7890; proxy_http_version 1.1; # Forward real client identity headers 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; # CRITICAL: Disable buffering for Server-Sent Events (SSE) proxy_buffering off; proxy_cache off; chunked_transfer_encoding off; # Prevent connection drops during quiet intervals proxy_read_timeout 86400s; proxy_send_timeout 86400s; }}Critical NGINX Directives Explained:
| Directive | Value | Purpose |
|---|---|---|
proxy_buffering | off | Mandatory for SSE. Disables response caching and ensures security events arrive immediately. |
proxy_cache | off | Prevents NGINX from caching live stream chunks. |
chunked_transfer_encoding | off | Prevents chunk framing overhead on persistent SSE streams. |
proxy_read_timeout | 86400s | Extends timeout to 24 hours so quiet periods do not disconnect the dashboard stream. |
client_max_body_size | 10M | Allows encrypted batch log deliveries from remote edge nodes (/api/v1/events). |
X-Real-IP & X-Forwarded-For | $remote_addr | Preserves original visitor and remote agent client IPs for threat dossier attribution. |
5. Reverse Proxy with Remote Edge Nodes (rwarden push)
If you are using Routewarden's distributed log shipping (rwarden push), remote agents connect to the dashboard URL:
# Register remote edge server with central dashboard reverse proxy URLrwarden register --url https://soc.example.com --key rw_sec_8f9a2b4c1e0d3f7a9b2c4e6f8a0d1c3e4f5a6b7c8d9e0f1a# Stream encrypted logs from containers or access logs in detached moderwarden push -dIngestion Security Architecture:
- Remote edge nodes send encrypted batches to
POST /api/v1/eventsand health pings toPOST /api/v1/heartbeat. - All payloads are encrypted using ECDH (Curve25519) + AES-GCM-256 using the server's public key.
- If you protect the web UI with reverse proxy basic auth or IP allowlisting, ensure
/api/v1/endpoints remain accessible to edge nodes:
# NGINX: Whitelist API ingestion for remote nodes while protecting the UIlocation /api/v1/ { proxy_pass http://127.0.0.1:7890; proxy_http_version 1.1; proxy_buffering off; client_max_body_size 10M; # No HTTP Basic Auth required here — agents authenticate via cryptographic tokens}location / { # Protect dashboard UI with corporate IP restriction or auth allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; proxy_pass http://127.0.0.1:7890; proxy_buffering off;}Built-In Cyber Auth
Routewarden includes its own built-in cyber authentication screen via --auth <user:password>. When using Routewarden's built-in auth, the /api/v1/* endpoints and static assets are automatically whitelisted, so you do not need complex reverse proxy location blocks.
6. Verification & Troubleshooting
Once your reverse proxy is active, verify that SSE streaming and event ingestion are functioning correctly:
- Test Web Dashboard: Open
https://soc.example.comin your browser. Verify the green 🟢 LIVE badge appears in the topbar. - Inspect SSE Stream in DevTools: Open Developer Tools → Network tab → Filter by
stream(/api/stream).- The connection should remain in
Pendingstate with status200 OK. - In the EventStream tab, new events should appear in real time without delay.
- The connection should remain in
- Check Edge Node Connectivity & Streaming:
# 1. Check edge node connectivity to ingestion endpointcurl -I https://soc.example.com/api/v1/heartbeat# Expected: HTTP/2 401 Unauthorized (proves endpoint is reachable & token-protected)# 2. Inspect live SSE streaming connectioncurl -N -H "Accept: text/event-stream" https://soc.example.com/api/stream# 3. Test API stats endpointcurl -s https://soc.example.com/api/stats | jq .total_events7. Environment Variables & .env Configuration
To avoid exposing passwords, domain names, or secrets in shell history or version-controlled Compose files, Routewarden reads configuration directly from environment variables.
Environment Variable Reference
| Variable | CLI Flag Equivalent | Default | Description |
|---|---|---|---|
RW_DASHBOARD_AUTH | -a, --auth | "" | HTTP Basic Auth credentials in user:password format for the cyber auth screen. |
RW_DASHBOARD_USER | --user | "" | Optional Basic Auth username (combined with RW_DASHBOARD_PASSWORD). |
RW_DASHBOARD_PASSWORD / RW_DASHBOARD_PASS | --password | "" | Optional Basic Auth password. |
RW_DASHBOARD_HOST | --host | "" | Host interface to bind (e.g. 127.0.0.1 for local reverse proxy isolation). |
PORT | --port | 7890 | TCP port for the dashboard HTTP server and SSE stream. |
GEOIP_DB / MAXMIND_DB | --geoip-db | Auto | Optional path to custom MaxMind GeoLite2-Country.mmdb or GeoLite2-City.mmdb. |
TZ | — | UTC | Timezone used for formatted timestamps and timeline aggregation. |
RWARDEN_MONITOR_WAIT | -w, --wait | false | When set to true, waits and retries if no matching containers are found initially. |
Configuration Examples (.env, Compose, Systemd)
# .env — Production Environment Configuration for Routewarden# ─────────────────────────────────────────────────────────────# Domain and NetworkDOMAIN=soc.example.comPORT=7890RW_DASHBOARD_HOST=127.0.0.1TZ=UTC# Cyber Auth Screen Credentials (user:password or separate user/pass)RW_DASHBOARD_AUTH=admin:SuperSecretPassphrase2026!# Or individually:# RW_DASHBOARD_USER=admin# RW_DASHBOARD_PASSWORD=SuperSecretPassphrase2026!# MaxMind GeoIP Database (Optional custom path)# GEOIP_DB=/root/.routewarden/dashboard/GeoLite2-Country.mmdb# Traefik / ACME SSL Notification EmailLETSENCRYPT_EMAIL=security@example.comEnvironment Precedence
CLI flags always take precedence over environment variables. For example, passing --auth admin:cli-pass overrides any RW_DASHBOARD_AUTH environment variable.