受欢迎的博客标签

How to Configure nginx as Reverse Proxy for Websocket in asp .net core blazor server

Published

How to configure nginx as reverse proxy for websocket

How to Configure NGINX to Proxy WebSockets

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