NGINX如何反向代理WebSockets并启用SSL(wss://)?

204

我对自己构建NGINX很迷茫和陌生, 但是我希望能够在没有额外层的情况下启用安全的WebSockets。

我不想在WebSocket服务器本身上启用SSL,而是希望使用NGINX为整个服务添加SSL层。

尽管每个网页都说我做不到,但我知道我能行!谢谢任何(包括我自己)能够指导我的人!

8个回答

283

请注意,nginx 从 1.3.13 版本开始支持 Websockets。以下是使用示例:

location /websocket/ {

    proxy_pass ​http://backend_host;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;

}

您还可以查看nginx更新日志WebSocket代理文档。


8
针对超时问题,proxy_read_timeout 是有效的。我编辑了这个答案。 - Steve Kehlet
3
这个配置应该放在哪里?backed_host是什么意思? - Aysennoussi
4
@Sekai: location指令位于server或另一个location指令内(参见location文档)。backend_host是一个upstream(参见upstream文档)- 一个或一组您将代理到的服务器。 - Radko Dinev
1
这个超时问题怎么办?我们真的需要将它设置为非常大的数字来避免吗?现在没有更优雅的解决方案了吗? - Mohammed Noureldin
如果我们正在使用https,为什么要编写“proxy_http_version”和“proxy_set_header Upgrade $http_upgrade”? - Normajean
显示剩余6条评论

85

4
我曾在反向代理后无法让TeamCity的WebSockets正常工作。你的# WebSocket support 代码片段解决了我的问题。我之前一直试图转发400端口,但是wss需要使用443端口。供未来的读者参考 :) - Mario Tacke
你解决了这个问题吗?因为我之前也遇到过类似的问题。 - Nɪsʜᴀɴᴛʜ ॐ
2
我最喜欢这个答案,因为很多人(比如你)都同时使用 / 作为Websockets和常规HTTP2的路径。 - mikemaccana
有谁知道调用JavaScript的代码吗? - Andrew Simpson
这对我和ShinobiCCTV通过unraid和反向代理起作用了。我发现Web套接字被阻止了。 - kcabrams
当我使用python-socketio包进行开发时,它非常有效。非常感谢。 - Milo Chen

59

别担心,一群勇敢的Ops程序员用全新的nginx_tcp_proxy_module解决了这个问题。

这篇文章是在2012年8月写的,所以如果你来自未来,你应该好好查阅资料。

前提条件

假设你使用的是CentOS:

  • 删除当前NGINX实例(建议使用开发服务器进行此操作)
  • 如果可能,请保存旧的NGINX配置文件,以便您可以重复使用它们(包括您的init.d/nginx脚本)
  • yum install pcre pcre-devel openssl openssl-devel和任何其他构建NGINX所需的库
  • 从GitHub https://github.com/yaoweibin/nginx_tcp_proxy_module 获取nginx_tcp_proxy_module,并记住放置它的文件夹(确保它没有被压缩)

构建您的新NGINX

同样,假设CentOS:

  • cd /usr/local/
  • wget 'http://nginx.org/download/nginx-1.2.1.tar.gz'
  • tar -xzvf nginx-1.2.1.tar.gz
  • cd nginx-1.2.1/
  • patch -p1 < /path/to/nginx_tcp_proxy_module/tcp.patch
  • ./configure --add-module=/path/to/nginx_tcp_proxy_module --with-http_ssl_module(如果需要,您可以添加更多模块)
  • make
  • make install

可选:

  • sudo /sbin/chkconfig nginx on

设置Nginx

记得先复制你的旧配置文件,如果你想重复使用它们。

重要提示:您需要在conf的最高级别创建一个tcp {}指令。确保它不在您的http {}指令中。

下面的示例配置显示单个上游websocket服务器以及两个代理,分别用于SSL和非SSL。

tcp {
    upstream websockets {
        ## webbit websocket server in background
        server 127.0.0.1:5501;
        
        ## server 127.0.0.1:5502; ## add another server if you like!

        check interval=3000 rise=2 fall=5 timeout=1000;
    }   

    server {
        server_name _;
        listen 7070;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }

    server {
        server_name _;
        listen 7080;

        ssl on;
        ssl_certificate      /path/to/cert.pem;
        ssl_certificate_key  /path/to/key.key;

        timeout 43200000;
        websocket_connect_timeout 43200000;
        proxy_connect_timeout 43200000;

        so_keepalive on;
        tcp_nodelay on;

        websocket_pass websockets;
        websocket_buffer 1k;
    }
}

6
这很有帮助,但我仍然在60秒后遇到了超时问题。我成功解决了这个问题,方法是设置以下内容: timeout 43200000; websocket_connect_timeout 43200000; websocket_read_timeout 43200000; websocket_send_timeout 43200000; proxy_connect_timeout 43200000; proxy_read_timeout 43200000; proxy_send_timeout 43200000; - jbg
1
我想在同一个HTTP端口上提供Websockets服务,并且只有在浏览器经过身份验证后才能使用。看起来这个方法无法处理在同一端口上的Websockets。人们是如何处理这种情况的? - uroc
1
需要进行一些软件修改才能检测到传入的协议。由于WebSockets实际上是从HTTP握手开始的(比TCP更高级的软件层),因此您必须调整应用程序以处理TCP和HTTP流量。我目前还无法推荐一种方法来做到这一点。 - crockpotveggies
3
如果其他2018年的人来到这里,这些指令已经不再适用。请前往http://nginx.org/en/docs/http/websocket.html获取最新说明或查看下面Harlan T Wood的答案。 - GaryO
你解决了这个问题吗?因为我之前也遇到过类似的问题。 - Nɪsʜᴀɴᴛʜ ॐ

26

使用SSL的.NET Core 2.0与Nginx

location / {
    # redirect all HTTP traffic to localhost:8080
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
}

这对我起了作用。


这是C#代码。我目前在Windows/IIS中使用以下代码:_server = new WebSocketServer("wss://0.0.0.0:8200/MessageRelayer") { Certificate = new X509Certificate2(PfxFileName, SslPassword), RestartAfterListenError = true }; - Andrew Simpson
我正在使用SignalR。 - Michika Iranga Perera

13

对我来说,关键在于proxy_pass位置设置。我需要切换到使用HTTPS协议,并在节点服务器端设置有效的SSL证书。这样,当我引入外部节点服务器时,我只需要更改IP地址,其他所有配置保持不变。

希望这可以帮助某些人…我一直在盯着这个问题看了好久…唉…

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
upstream nodeserver {
        server 127.0.0.1:8080;
}
server {
        listen 443 default_server ssl http2;
        listen [::]:443 default_server ssl http2 ipv6only=on;
        server_name mysite.com;
        ssl_certificate ssl/site.crt;
        ssl_certificate_key ssl/site.key;
        location /websocket { #replace /websocket with the path required by your application
                proxy_pass https://nodeserver;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_http_version 1.1;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_intercept_errors on;
                proxy_redirect off;
                proxy_cache_bypass $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-NginX-Proxy true;
                proxy_ssl_session_reuse off;
            }
}

1
我尝试了localtion/horizon,但它没有起作用。只有localtion /localtion/websockify有效。不知道为什么... - njuguoyi
如果将“proxy_set_header Connection $connection_upgrade;”替换为“proxy_set_header Connection $http_connection;”,则可以正常工作。 - Xie Yanbo

9

使用nginx/1.14.0

我有一个websocket服务器运行在8097端口,用户从8098端口连接到wss,nginx只是解密内容并将其转发给websocket服务器。

因此,我有这个配置文件(在我的情况下是/etc/nginx/conf.d/default.conf

server {
    listen   8098;
        ssl on;
        ssl_certificate      /etc/ssl/certs/domain.crt;
        ssl_certificate_key  /root/domain.key;
    location / {

        proxy_pass http://hostname:8097;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;

    }
}

3
在2022年,这对我帮助很大。在添加读取超时之前,WSS/WS什么都不起作用。 - gwhiz

9
一个简洁好文章由Pankaj Malhotra撰写,讨论如何使用NGINX实现此功能,可在这里找到。
以下是基本的NGINX配置:
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream appserver {
    server 192.168.100.10:9222; # appserver_ip:ws_port
}

server {
    listen 8888; // client_wss_port

    ssl on;
    ssl_certificate /path/to/crt;
    ssl_certificate_key /path/to/key;


    location / {
        proxy_pass http://appserver;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

1
现代版本的NGINX是否也解决了超时问题? - crockpotveggies

4
如果你想在你的测试环境中添加SSL,那么你可以使用mkcert。下面是GitHub的网址。
https://github.com/FiloSottile/mkcert
还有下面是反向代理的示例nginx配置。
server {
        listen 80;
        server_name test.local;
        return 301 https://test.local$request_uri;
}
server {
  listen 443 ssl;
  server_name test.local;
  ssl_certificate /etc/nginx/ssl/test.local.pem;
  ssl_certificate_key /etc/nginx/ssl/test.local-key.pem;

   location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Client-Verify SUCCESS;
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://localhost:3000;
      proxy_redirect off;
      proxy_buffering off;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接