Streaming cheatsheet.
Server-Sent Events (SSE)
location /events {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 24h;
proxy_send_timeout 24h;
chunked_transfer_encoding off;
add_header X-Accel-Buffering no;
}
X-Accel-Buffering: no also tells nginx not to buffer (if set by backend).
WebSocket
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /ws {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Long polling
location /poll {
proxy_pass http://app;
proxy_buffering off;
proxy_read_timeout 60s;
}
gRPC streaming
server {
listen 50051 http2;
location / {
grpc_pass grpc://backend:50052;
grpc_read_timeout 24h;
grpc_send_timeout 24h;
}
}
Streaming LLM responses
OpenAI-style:
location /v1/chat/completions {
proxy_pass http://llm-backend;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_read_timeout 300s;
}
proxy_request_buffering off important for upload streaming.
Disable response buffering globally
proxy_buffering off;
But typically you want buffering enabled for normal HTTP — disable per-location.
Time-out tuning for long connections
keepalive_timeout 86400;
client_body_timeout 86400;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
Connections per IP for WebSocket
limit_conn_zone $binary_remote_addr zone=ws_conn:10m;
location /ws {
limit_conn ws_conn 5; # max 5 ws per IP
...
}
stream module (TCP/UDP)
For raw TCP (e.g., MQTT):
stream {
upstream mqtt {
server 10.0.0.1:1883;
}
server {
listen 1883;
proxy_pass mqtt;
proxy_timeout 1h;
}
server {
listen 8443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
proxy_pass mqtt;
}
}
UDP
stream {
server {
listen 53 udp;
proxy_pass dns_backend;
proxy_responses 1;
proxy_timeout 5s;
}
}
Stream logging
stream {
log_format basic '$remote_addr [$time_local] $protocol $status $bytes_sent $bytes_received $session_time';
access_log /var/log/nginx/stream.log basic;
}
Stream UPGRADE (mixed)
For TLS termination of WebSocket (alternative pattern with stream module + sni):
stream {
map $ssl_preread_protocol $upstream {
"TLSv1.2" tls_backend;
"TLSv1.3" tls_backend;
default plain_backend;
}
server {
listen 443;
ssl_preread on;
proxy_pass $upstream;
}
}
Common mistakes
proxy_buffering on(default) for SSE → response delayed until full.- WebSocket without
Upgrade/Connectionheaders. - Short
proxy_read_timeoutfor long-lived connections. proxy_passmid-stream causing reconnect.gzip onon SSE → broken (chunked sse with gzip flush issues).
Read this next
If you want my SSE/WS proxy templates, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .