C.W.K.
Stream
Lesson 03 of 07 · published

Reverse Proxy Configuration

~11 min · production, nginx, caddy

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

WebSocket proxying is not the default

By default, most HTTP-1.1 reverse proxies do not forward the Upgrade header, which means the WebSocket handshake fails with a 400 from the proxy itself. You have to explicitly enable it. Once enabled, increase idle timeouts so long-lived WebSocket connections do not get killed mid-session.

Nginx vs Caddy

Nginx requires explicit proxy_set_header Upgrade $http_upgrade and Connection $connection_upgrade. Caddy detects WebSocket and forwards everything automatically — no special config. For a quick proof-of-concept, Caddy wins on ergonomics; Nginx is the default in production for reasons of familiarity, not capability.

Code

Nginx full WebSocket proxy·nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/ssl/example.com.crt;
    ssl_certificate_key /etc/ssl/example.com.key;

    location /ws {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;

        # Forward upgrade headers
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Standard 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;

        # Long-lived connections need long timeouts
        proxy_read_timeout  3600s;
        proxy_send_timeout  3600s;

        # Disable buffering for streaming
        proxy_buffering off;
    }
}
Caddy — same job, ten lines·text
api.example.com {
    reverse_proxy /ws/* websocket_backend:8000
    # Caddy auto-handles Upgrade headers and TLS via Let's Encrypt.
}

External links

Exercise

Stand up Nginx in front of your FastAPI WebSocket server. Forget the Upgrade header on purpose — observe the 400 response. Add it back; confirm the handshake succeeds. Then set proxy_read_timeout to 30s and watch your idle connection get killed every 30s. Set it back to 3600s.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.