Reverse Proxy
Use this page if you run Varin behind Nginx, Nginx Proxy Manager, Caddy, Cloudflare, or another reverse proxy.
Before you proxy it
Section titled “Before you proxy it”- Confirm Varin works directly first.
- Open
http://<server-ip>:3000or your custom port from the same network. - Only add the reverse proxy after the direct connection works.
What the proxy must support
Section titled “What the proxy must support”- WebSocket:
/api/dictation/ws/api/terminal/ws/api/varin/realtime-proxy/ws
- SSE without buffering:
/api/notifications/stream/api/varin/events/api/threads/observe/api/varin/realtime-proxy/sse
- Large request bodies for attachments and file operations
- Long-lived read timeouts for live streams and terminal sessions
Rules that matter
Section titled “Rules that matter”- Enable WebSocket proxying.
- Disable buffering on SSE routes.
- Disable gzip on the proxy if Varin is already compressing responses.
- Keep compression enabled in only one layer.
- Forward normal proxy headers such as
Host,X-Forwarded-For, andX-Forwarded-Proto. - Increase body size limits if users upload files.
Quick checklist
Section titled “Quick checklist”- Varin reachable directly on LAN
- WebSockets enabled in the proxy
- SSE routes have buffering off
gzip offon the proxy host, or proxy compression disabled another wayclient_max_body_sizelarge enough for attachmentsproxy_read_timeoutlong enough for streams
Example: Nginx
Section titled “Example: Nginx”Show example config
client_max_body_size 50M;client_body_buffer_size 50M;proxy_request_buffering off;
proxy_http_version 1.1;proxy_set_header Connection "";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;proxy_set_header X-Forwarded-Host $host;
gzip off;
location ~ ^/api/(varin/realtime-proxy|terminal|dictation)/ws$ { proxy_pass http://127.0.0.1:3000; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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; proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; proxy_send_timeout 3600s;}
location ~ ^/api/(threads/observe|notifications/stream|varin/(events|realtime-proxy/sse))$ { proxy_pass http://127.0.0.1:3000; 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; proxy_set_header Accept "text/event-stream"; proxy_set_header Cache-Control "no-cache"; proxy_buffering off; proxy_cache off; gzip off; add_header X-Accel-Buffering "no" always; add_header Cache-Control "no-cache, no-transform" always; proxy_read_timeout 3600s; proxy_send_timeout 3600s;}
location /api { proxy_pass http://127.0.0.1:3000; proxy_read_timeout 3600s; proxy_send_timeout 3600s;}
location / { proxy_pass http://127.0.0.1:3000;}Example: Nginx Proxy Manager
Section titled “Example: Nginx Proxy Manager”Enable Websockets Support in Nginx Proxy Manager. Use the buffering, request-size and timeout directives from the Nginx example above in the host’s Advanced configuration; keep the same current WebSocket and SSE paths.
Common failure signs
Section titled “Common failure signs”Page loads, but sending messages fails
Section titled “Page loads, but sending messages fails”- Check authentication and forwarding for
POST /api/threads/*requests. - Check that
GET /api/threads/observestreams SSE without buffering or caching.
Notifications or live status do not update
Section titled “Notifications or live status do not update”- one of the SSE routes is buffered or cached
X-Accel-Buffering "no"is missing
File uploads fail
Section titled “File uploads fail”client_max_body_sizeis too small
Everything works locally, but breaks only behind the proxy
Section titled “Everything works locally, but breaks only behind the proxy”- the proxy is compressing and buffering live traffic
- the proxy is missing WebSocket support
Example: Caddy
Section titled “Example: Caddy”Show example config
reverse_proxy 127.0.0.1:3000 { # WebSocket support is automatic in Caddy
# Flush SSE responses immediately flush_interval -1
# Pass through Host and proxy headers header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme}
# Increase timeouts for long-lived streams transport http { read_timeout 3600s write_timeout 3600s }}Caddy handles WebSocket upgrades automatically — no extra configuration needed. The flush_interval -1 directive ensures SSE chunks are forwarded immediately without buffering.
CDN and double-compression warning
Section titled “CDN and double-compression warning”If you place a CDN (such as Cloudflare) in front of your reverse proxy, be aware of double compression:
- Varin compresses HTTP responses with gzip (threshold 1 KB).
- Cloudflare and other CDNs also compress responses by default.
- This can cause double-compressed responses or incorrect
Content-Encodingheaders.
To avoid this, disable compression at one layer:
- Cloudflare: Rules → Compression → disable (or use “Passthrough” mode).
- Nginx:
gzip off(already shown in the examples above). - Caddy: Caddy does not re-compress by default if the upstream already sends compressed content.
SSE streaming routes are excluded from compression by Varin, but the CDN may still buffer them. Check your CDN documentation for how to disable buffering on SSE paths.