NGINX 双代理不允许 websocket 连接

Sim*_*ard 4 nginx websocket

我的机器上设置了两个 nginx 代理,一个用于解包 SSL,另一个用于执行特定于应用程序的代理(只有第二个是版本控制的)。当我只有一个代理时,我能够成功建立 Websocket 连接,但在转移到两个代理后,所有 Websocket 升级请求都会响应 502 Bad Gateway 错误。我可以确认正常的 http/https 请求适用于我的双代理设置。这是我当前的配置。

代理1

server {

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        server_name staging.ambitx.io;

        location / {
                proxy_pass http://127.0.0.1:81;
                include proxy_params;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/staging.ambitx.io/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/staging.ambitx.io/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

}
server {
    if ($host = staging.ambitx.io) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

        listen 80 default_server;
        listen [::]:80 default_server;

        server_name staging.ambitx.io;
    return 404; # managed by Certbot

}
Run Code Online (Sandbox Code Playgroud)

代理参数

proxy_set_header Host $http_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;
Run Code Online (Sandbox Code Playgroud)

代理2

(在docker上运行,主机上的端口81绑定到容器上的端口80)

resolver 127.0.0.11 ipv6=off;

server {
  listen       80;
  listen  [::]:80;

  location / {
    root /var/www/staticfiles;
    index  index.html index.htm;
    try_files $uri /index.html =404;
  }
  
  location /ws {
    access_log off;

    proxy_pass       http://wsserver;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

  location /api {
    proxy_pass http://apiserver;
  }
}
Run Code Online (Sandbox Code Playgroud)

location /ws我最初在Proxy 2 的配置块中有以下语句...

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Run Code Online (Sandbox Code Playgroud)

但我删除了它们,因为它们会覆盖 Proxy 1 设置的标头。

有任何想法吗?如果您需要更多信息,请告诉我。

Iva*_*sky 5

当然,您不应该从任何中间代理服务器中删除 WebSocket 代理支持。WebSocket 代理是一项非常特殊的任务,为了允许协议切换并保持 WebSocket 连接的建立和活动,您也应该将其支持返回给您的第一个 nginx 代理:

...
location / {
    proxy_pass http://127.0.0.1:81;
    include proxy_params;
}
location /ws {
    proxy_pass http://127.0.0.1:81;
    include proxy_params;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
...
Run Code Online (Sandbox Code Playgroud)

从第二个 nginx 代理中删除所有三个proxy_set_header指令也是一个坏主意。虽然X-Real-IPX-Forwarded-For标头将完全按照您的第一个代理设置的方式传递到您的 WebSocket 应用程序,但该Host标头是一个特殊的标头。除非显式设置,否则它将传递等于proxy_pass指令中使用的上游名称,即Host: wsserver。正如您可以从proxy_set_header指令文档中读到的:

默认情况下,仅重新定义两个字段:

proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Run Code Online (Sandbox Code Playgroud)

(第二个肯定也会破坏任何建立 WebSocket 连接的尝试)。Host因此,要保留客户端请求中的原始标头(通常是个好主意,您可以在此处阅读有关此标头的更多信息),请返回

proxy_pass Host $http_host;
Run Code Online (Sandbox Code Playgroud)

到第二个 nginx 代理配置的location / { ... }和。location /ws { ... }