【问题标题】:Nginx WebSocket proxying keep getting HTTP 301 redirectsNginx WebSocket 代理不断获得 HTTP 301 重定向
【发布时间】:2015-11-02 14:35:00
【问题描述】:

在过去的几天里,我一直在尝试让 Nginx WebSocket 代理工作,但在我的一生中,我无法让它工作。我遵循官方指南here 并一直使用Python 的websockets 模块作为服务器和一个npm 包wscat 作为客户端。从wscat 到 Python WebSocket 后端的直接连接工作正常(来自浏览器的连接也是如此)。但是,一旦我在 Nginx 中分层,它就无法正常工作并继续给我一个标准的 HTTP 301 重定向。

使用 Nginx 代理的 cURL 调试输出:

$ curl 'http://test.ws:8080/websocket' \
> -H 'Pragma: no-cache' \
> -H 'Origin: http://localhost:8080' \
> -H 'Accept-Encoding: gzip, deflate, sdch' \
> -H 'Sec-WebSocket-Version: 13' \
> -H 'Sec-WebSocket-Key: V15bszpaQ+8Vq7mWR6NQbQ==' \
> -H 'User-Agent: Mozilla/5.0' \
> -H 'Upgrade: websocket' \
> -H 'Cache-Control: no-cache' \
> -H 'Connection: Upgrade' \
> -H 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits' \
    --compressed -v
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to test.ws (127.0.0.1) port 8080 (#0)
> GET /websocket HTTP/1.1
> Host: test.ws:8080
> Accept: */*
> Pragma: no-cache
> Origin: http://localhost:8080
> Accept-Encoding: gzip, deflate, sdch
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Key: V15bszpaQ+8Vq7mWR6NQbQ==
> User-Agent: Mozilla/5.0
> Upgrade: websocket
> Cache-Control: no-cache
> Connection: Upgrade
> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
> 
< HTTP/1.1 301 Moved Permanently
* Server nginx/1.8.0 is not blacklisted
< Server: nginx/1.8.0
< Date: Mon, 10 Aug 2015 15:04:26 GMT
< Content-Type: text/html
< Content-Length: 184
< Location: http://test.ws:8080/websocket/
< Connection: keep-alive
< 
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.8.0</center>
</body>
</html>
* Connection #0 to host test.ws left intact

cURL 调试输出不带 Nginx 代理:

$ curl 'http://test.ws:8765/' \
> -H 'Pragma: no-cache' \
> -H 'Origin: http://localhost:8080' \
> -H 'Accept-Encoding: gzip, deflate, sdch' \
> -H 'Sec-WebSocket-Version: 13' \
> -H 'Sec-WebSocket-Key: V15bszpaQ+8Vq7mWR6NQbQ==' \
> -H 'User-Agent: Mozilla/5.0' \
> -H 'Upgrade: websocket' \
> -H 'Cache-Control: no-cache' \
> -H 'Connection: Upgrade' \
> -H 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits' \
> --compressed -v
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to test.ws (127.0.0.1) port 8765 (#0)
> GET / HTTP/1.1
> Host: test.ws:8765
> Accept: */*
> Pragma: no-cache
> Origin: http://localhost:8080
> Accept-Encoding: gzip, deflate, sdch
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Key: V15bszpaQ+8Vq7mWR6NQbQ==
> User-Agent: Mozilla/5.0
> Upgrade: websocket
> Cache-Control: no-cache
> Connection: Upgrade
> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
> 
< HTTP/1.1 101 Switching Protocols
* Server Python/3.4 websockets/2.5 is not blacklisted
< Server: Python/3.4 websockets/2.5
< Upgrade: WebSocket
< Connection: Upgrade
< Sec-WebSocket-Accept: yR97tmHAm9KPEI5vfKiM0/sfTqQ=
^C

我的 Nginx(版本1.8.0)配置是这样的(websockets 在端口 8765 上的 test.ws (127.0.0.1) 上运行)并且 nginx 正在侦听 localhost 上的端口 8080:

worker_processes  1;

error_log  logs/error.log debug;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    upstream ws {
        server 127.0.0.1:8765;
    }

    # map $http_upgrade $connection_upgrade {
    #     default upgrade;
    #     ''      close;
    # }

    server {
        listen       8080;
        server_name  test.ws;

        access_log  logs/localhost.access.log;

        location / {
            root   html;
            index  index.html index.htm;
        }

        location /websocket/ {
            proxy_pass http://ws;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

}

include servers/*;

任何人都知道我在这里做错了什么以及为什么给我 301 重定向?

【问题讨论】:

  • 您是否尝试过从位置 /websocket/ 更改为位置 /websocket(没有尾随 /)?

标签: python curl nginx proxy websocket


【解决方案1】:

在我的情况下,它来自使用路径 /websocket 而不是 /websocket/ 的客户端,从而阻止了位置规则的启动。 但是位置顺序的影响是一个很好的提示!

【讨论】:

    【解决方案2】:

    注意你的标题。在浏览器和客户端发送的header中,是Upgrade而不是upgrade

    改变这个:

        location /websocket/ {
            proxy_pass http://ws;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    

        location /websocket/ {
            proxy_pass http://ws;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }
    

    注意大写Upgrade

    【讨论】:

    • 我认为这不重要。
    • 你是对的,它们应该是不区分大小写的,但在许多情况下它们不是。许多浏览器不接受不大写的标题。就我而言,当使用小写的“升级”时,nginx 会拒绝传递该标头。将其更改为大写是唯一有帮助的想法。 There's a discussion on Stack Overflow about this
    • 另请注意,在正式使用时,“升级”必须大写。见MSDN article
    • 没有任何区别。
    【解决方案3】:

    事实证明,我的 Nginx 配置中的根位置节一直在干扰 WebSocket 代理传递隧道。有三种方法可以解决这个问题。

    1. 修改根位置节以完全匹配。

      location = / {
          root   html;
          index  index.html index.htm;
      }
      
    2. 将根位置节重新排序为 websocket 位置节之后(特定的在前,一般的在后,就像重写匹配规则的工作原理一样)。

    3. 在 Nginx 配置中删除以下根位置节将使其工作。

      location / {
          root   html;
          index  index.html index.htm;
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-13
      • 2020-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多