How to configure nginx as reverse proxy for websocket
How to Configure NGINX to Proxy WebSockets
Table of Contents
Table of Content
step 1.Create the Nginx Reverse Proxy
Configure nginx as a typical reverse proxy configuration
step 2.Add code for WebSocket protocol
Sets the connections switch to the WS and WSS protocols
step 3. Add code for proxy_read_timeout
step 4. Add code for self keepalive task
Offical doc
Blazor server
httpclient request https://www.youdomain.com)->nginx->http://localhost:8100(.net core Blazor server)
nginx version
nginx version>= 1.3.13
Since version 1.3.13, nginx implements WebSocket protocol。
ginx从1.3.13版本开始支持websocket协议,主要是通过在配置中增加请求头信息.
#nginx -V
nginx version: nginx/1.14.0 (Ubuntu)
built with OpenSSL 1.1.1 11 Sep 2018
TLS SNI support enabled
Linux with Nginx minimum required settings to enable WebSockets
https://docs.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-6.0#linux-with-nginx
Step 1. Configure nginx as a typical reverse proxy configuration
step 2. Add code for WebSocket protocol
vi /etc/nginx/sites-enabled/default.conf
http {
upstream ws-backend {
server 127.0.0.1:4432 max_fails=0 weight=1;
}
server {
server_name *.example.com; # managed by Certbot
listen 443 ssl; # managed by Certbot
location / {
proxy_pass http://ws-backend;
proxy_set_header Host $host;
proxy_set_header X_Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#ws websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
}
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
}
Understanding the Configuration(More Detail)
http {
#part 1
upstream ws-backend {
server 127.0.0.1:4432 max_fails=0 weight=1;
}
#part 2
server {
#The following configuration creates a virtual host within NGINX, which listens for requests on port 443 for hostname *.example.com.
server_name *.example.com; # managed by Certbot
listen 443 ssl; # managed by Certbot
#Next is the location directive, within the server directive, matches request URIs to be processed. In the example, a ‘/’ represents a catchall for URIs sent.
location / {
#This directive instruct NGINX proxy all requests matching the location pattern to an upstream (backend) server. In the example given, ws-backend is used, however, this is actually the name of an upstream group created further down in the configuration.
proxy_pass http://ws-backend;
#Sets the Host header to be that of the NGINX server.
proxy_set_header Host $host;
proxy_set_header X_Real_IP $remote_addr;
#Ensure the IP of the client sending requests to the NGINX is stored in the request header. This allows connections to be traced back to an origin. Without adding this header, all requests to the upstream WebSocket servers will have the NGINX server’s IP address as the source.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#------The next few options are the magic that enable WebSocket support. These must exist for the NGINX to correctly proxy WebSocket requests to upstream WebSocket servers.------
#ws websocket
#This directive converts the incoming connection to HTTP 1.1, which is required to support WebSockets. The older HTTP 1.0 spec does not provide support for WebSockets, and any requests using HTTP 1.0 will fail.
proxy_http_version 1.1;
#Converts the proxied connection to type Upgrade. WebSockets only communicate on Upgraded connections.
proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
}
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
}
First,This is a typical reverse proxy configuration.
Second,It begins with setting headers that allow client information to pass through the proxy into the upstream WebSocket servers.
Restart the NGINX server
Saving your settings and then restart the NGINX server will enable it to support WebSocket connections.
step 3: Understanding the Configuration
1. HTTP/1.1
To turn a connection between a client and server from HTTP/1.1 into WebSocket, the protocol switchmechanism available in HTTP/1.1 is used
2.WebSocket protocol
The WebSocket protocol is different from the HTTP protocol, but the WebSocket handshake is compatible with HTTP, using the HTTP Upgrade facility to upgrade the connection from HTTP to WebSocket.
在 HTTP 的标准里面,在 HTTP 协议提供了一种特殊的机制,这一机制允许将一个已建立的连接升级成新的、不相容的协议。由客户端发起给服务端询问可以服务器端选择是否要升级到新协议,这个机制可以做到如客户端使用HTTP/1.1去连接服务器端,询问服务器端是否能升级到HTTP2甚至是WebSockets协议。
# proxy_set_header Connection "Upgrade";
For every established websocket connection, you will be able to see:
2019/10/01 23:01:45 [debug] 15851#15851: *7 recv: fd:9 129 of 4096
2019/10/01 23:01:45 [debug] 15851#15851: *7 http proxy status 101 "101 Switching Protocols"
2019/10/01 23:01:45 [debug] 15851#15851: *7 http proxy header: "Upgrade: websocket"
2019/10/01 23:01:45 [debug] 15851#15851: *7 http proxy header: "Connection: Upgrade"
2019/10/01 23:01:45 [debug] 15851#15851: *7 http proxy header: "Sec-WebSocket-Accept: jBOkltuW7IXfgy0yPrHgbT5RSl4="
2019/10/01 23:01:45 [debug] 15851#15851: *7 http proxy header done
2019/10/01 23:01:45 [debug] 15851#15851: *7 HTTP/1.1 101 Switching Protocols
3. connection will be closed idle within 60 seconds
By default, the connection will be closed if the proxied server does not transmit any data within 60 seconds. This timeout can be increased with the proxy_read_timeout directive. Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive.
On connect:
2019/10/03 07:14:14 [debug] 21#21: *1762 recv: fd:18 166 of 4096
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy status 101 "101 Switching Protocols"
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy header: "Connection: Upgrade"
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy header: "Date: Thu, 03 Oct 2019 07:14:13 GMT"
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy header: "Upgrade: websocket"
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy header: "Sec-WebSocket-Accept: Imw3+38E5f9bdC2xn9qYDlMs4yw="
2019/10/03 07:14:14 [debug] 21#21: *1762 http proxy header done
2019/10/03 07:14:14 [debug] 21#21: *1762 HTTP/1.1 101 Switching Protocols
On timeout
2019/10/03 07:16:11 [debug] 21#21: epoll timer: 2847
2019/10/03 07:16:14 [debug] 21#21: timer delta: 2848
2019/10/03 07:16:14 [debug] 21#21: *1762 event timer del: 18: 7879807788
2019/10/03 07:16:14 [debug] 21#21: *1762 http upstream request: "/api/chat/connect?token=6cdcf634-2440-428f-a8bf-f0e28675b2e4"
2019/10/03 07:16:14 [debug] 21#21: *1762 http upstream process upgraded, fu:1
2019/10/03 07:16:14 [info] 21#21: *1762 upstream timed out (110: Operation timed out) while proxying upgraded connection, client: 127.0.0.1, server: mydomain.com, request: "GET /api/chat/connect?token=6cdcf634-2440-428f-a8bf-f0e28675b2e4 HTTP/1.1", upstream: "http://10.244.2.220:80/api/chat/connect?token=6cdcf634-2440-428f-a8bf-f0e28675b2e4", host: "mydomain.com"
2019/10/03 07:16:14 [debug] 21#21: *1762 finalize http upstream request: 504
2019/10/03 07:16:14 [debug] 21#21: *1762 finalize http proxy request
2019/10/03 07:16:14 [debug] 21#21: *1762 free rr peer 2 0
2019/10/03 07:16:14 [debug] 21#21: *1762 close http upstream connection: 18
2019/10/03 07:16:14 [debug] 21#21: *1762 free: 000055F9725C7920, unused: 48
"Connection: upgrade" causes 400 error that never reaches application code. Triggered by common nginx config.
If a Kestrel receives a Connection: upgrade header, in a POST request with a nonempty body, then Kestrel will never pass the request to application code. It will give a 400 "Bad Request" response with an empty body.
client
GET /foo HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: example/1, foo/2
dbug: Microsoft.AspNetCore.Server.Kestrel[39]
Connection id "0HM5U310J8F34" accepted.
dbug: Microsoft.AspNetCore.Server.Kestrel[1]
Connection id "0HM5U310J8F34" started.
dbug: Microsoft.AspNetCore.Server.Kestrel[17]
Connection id "0HM5U310J8F34" bad request data: "Requests with 'Connection: Upgrade' cannot have content in the request body."
Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Requests with 'Connection: Upgrade' cannot have content in the request body.
at Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException.Throw(RequestRejectionReason reason)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1MessageBody.For(HttpVersion httpVersion, HttpRequestHeaders headers, Http1Connection context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1Connection.CreateMessageBody()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
Troubleshoot
Websocket proxy timeout
Nginx代理webSocket时60s自动断开, 怎么保持长连接(nginx延长超时时间的基础上,前端在超时时间内发心跳包)
Nginx代理webSocket经常中断的解决方案, 如何保持长连接
Websocket proxy timeout in github.com
400
How to Debugging a HTTP 400 Bad Request error in Nginx
Useful links
https://www.nginx.com/blog/websocket-nginx/
https://cloud.tencent.com/developer/article/1780373