Reverse Proxy
Dubby listens on HTTP only and does not terminate TLS itself. A reverse proxy sits in front of Dubby to handle HTTPS, route traffic, and forward the real client IP for rate limiting and audit logs.
Before you start
Section titled “Before you start”Dubby needs two headers from your proxy to identify clients correctly:
| Header | Purpose |
|---|---|
X-Forwarded-For | Client IP for rate limiting |
X-Real-IP | Client IP fallback (Nginx-style) |
Dubby also uses Server-Sent Events for real-time updates. Make sure your proxy does not buffer SSE responses and that its read timeout exceeds 30 seconds (Dubby’s default keepalive interval).
In the examples below, replace these values with your own:
| Placeholder | Example |
|---|---|
DOMAIN_NAME | dubby.example.com |
SERVER_IP_ADDRESS | 192.168.1.100 |
DUBBY_PORT | 3000 |
Caddy (recommended)
Section titled “Caddy (recommended)”Caddy is the simplest option — it handles HTTPS certificates automatically via Let’s Encrypt with no extra configuration.
dubby.example.com { reverse_proxy SERVER_IP_ADDRESS:3000}That’s it. Caddy provisions a certificate, redirects HTTP to HTTPS, and sets the correct proxy headers by default.
For WebSocket-style keepalive on SSE connections, Caddy’s defaults work without changes.
Caddy in Docker Compose
Section titled “Caddy in Docker Compose”services: caddy: image: caddy:2 restart: unless-stopped ports: - '80:80' - '443:443' volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config
volumes: caddy-data: caddy-config:server { listen 80; server_name dubby.example.com; return 301 https://$server_name$request_uri;}
server { listen 443 ssl; server_name dubby.example.com;
ssl_certificate /etc/letsencrypt/live/dubby.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/dubby.example.com/privkey.pem;
location / { proxy_pass http://SERVER_IP_ADDRESS:3000;
# Standard proxy 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;
# SSE support — disable buffering and extend timeouts proxy_buffering off; proxy_cache off; proxy_read_timeout 86400s; proxy_send_timeout 86400s; }}Traefik
Section titled “Traefik”Labels (Docker Compose)
Section titled “Labels (Docker Compose)”If Traefik manages your Docker containers via labels:
services: dubby: image: dubbytv/dubby:stable labels: - 'traefik.enable=true' - 'traefik.http.routers.dubby.rule=Host(`dubby.example.com`)' - 'traefik.http.routers.dubby.entrypoints=websecure' - 'traefik.http.routers.dubby.tls.certresolver=letsencrypt' - 'traefik.http.services.dubby.loadbalancer.server.port=3000' # Disable response buffering for SSE - 'traefik.http.middlewares.dubby-buffering.buffering.maxResponseBodyBytes=0' - 'traefik.http.routers.dubby.middlewares=dubby-buffering'File provider
Section titled “File provider”If you use Traefik’s file-based configuration:
http: routers: dubby: rule: 'Host(`dubby.example.com`)' entryPoints: - websecure service: dubby tls: certResolver: letsencrypt
services: dubby: loadBalancer: servers: - url: 'http://SERVER_IP_ADDRESS:3000'Verifying your setup
Section titled “Verifying your setup”After configuring your proxy, verify the headers are forwarded correctly:
- Open your browser’s developer tools (Network tab)
- Load your Dubby instance through the proxy
- Check the server logs — rate-limited requests should show your real IP, not the proxy’s
You can also hit the health endpoint directly:
curl -I https://dubby.example.com/health/A working setup returns 200 OK with content-type: application/json.